Java concurrency begining

26
Java concurrency - beginning Stetsenko Maksym [email protected]

Transcript of Java concurrency begining

Page 1: Java concurrency   begining

Java concurrency - beginning

Stetsenko [email protected]

Page 2: Java concurrency   begining

Lead-inObjective of concurrency - optimal utilization of system resources

Advantages - increases bandwidth of applications

Drawbacks - increases the complexity of the design, testing and maintenance

Used in:

Parallel processing algorithms

Network applications, asynchronous I / O access

GUI

Page 3: Java concurrency   begining

How to create thread in java ?

2. Implement Runnable

Advantages:- Inheritance from another object is still available

1. Extends Thread:

Advantages:- full access to functionality of the thread

Disadvantages: - can not be inherited from another class, but delegation mechanism can be used alternatively

void run() - must be overridden in both cases

void start() - causes this thread to begin execution; the JVM calls the run method of this thread. Can be called only once for each thread.

Page 4: Java concurrency   begining

Race condition

Race Condition is a type of flaw in an electronic or software system where the output is depend on the sequence or timing of other events.

Preconditions:

Two or more threads are sharing the same data.

At least one thread performs modification.

Example:// Thread 2:

while (!stop)

{

if (x%2 == 0)

System.out.println("x=" + x);

}

int x;

// Thread 1:

while (!stop)

{

x++;

}

Let's assume x=0. Suppose execution of program is performed in such way:

1. if - checks x for parity in thread 2.2. Operator «x++» increases x in thread 1.3. System.out.println in thread 2 displays «x=1». How come ? (We have already checked x for parity ).

Synchronization is needed, isn't it ? :)

Page 5: Java concurrency   begining

MonitorMonitor – it's an object, which is used for mutually exclusive lock.

Any object is a monitor in Java. This ability is inherited from Object class.

When one thread comes into synchronized block, it acquires a lock. All other threads have to wait until lock will be released.

Lock can be released in such cases:

- Synchronized block was successfully completed.

- Exception has been thrown.

Control under the monitor can be passed into other synchronized block:

- in all previous cases

- and when method wait has been called. (dead-clock state dangerous)

note: If static block or method is synchronized, then lock of the class is acquired. Access to static fields and methods is controlled by lock of the class that is different from lock of each instance.

Examples of lock acquiring:

Acquiring the lock of this: Acquiring the lock of another object (lock1):

public synchronized void addName() {...}

public void addName() { synchronized(this) { ...

}

nameList.add("Bob");}

public class MуClass { private long c1 = 0; private Object lock1 = new Object(); public void inc1() { synchronized(lock1) { c1++; } }

Page 6: Java concurrency   begining

Basic tools: wait, notify, notifyAll

public final void wait() / wait(long timeout) / notify () / notifyAll () - defined in Object class let us manage the thread state:

wait() – moves current thread to waiting state, releases the lock.

wait(timeout) - moves current thread to waiting state for specified time. Exit will be performed either:

• time elapsed;

• notification is received.

notify() / notifyAll() – notifies waiting thread / all waiting threads on this object's monitor that they can try to acquire the lock. Notification will be received by threads that have previously called wait. It's up to OS which thread

will be resumed.

note: wait() и notify(), notifyAll() must be called for acquired lock, otherwise java.lang.IllegalMonitorStateException will be thrown.

Applying autoboxing and enums as locks are slippery:

public class test {

static Integer foo = new Integer(1);

public static void main(String[] args) {

synchronized(foo) {

foo++;

foo.notifyAll(); }

}

}

RESULT:

Exception in thread "main" java.lang.IllegalMonitorStateException

at java.lang.Object.notifyAll(Native Method)

at test.main(test.java:6)

...

Page 7: Java concurrency   begining

Basic tools: sleep, join

public static void sleep(long millis) - Causes the currently executing thread to sleep for the specified number of milliseconds. Method doesn't release the lock.

public final void join() throws InterruptedExeption – waits for this thread to die. A lock doesn't have to be pre-acquired.

Defined in Thread class:

public class JoinTest { public static void main(String[] args) { Thread thread = new Thread() { @Override public void run() { System.out.println( Thread.currentThread().getName() + " is running"); try { TimeUnit.SECONDS.sleep(4); } catch (InterruptedException e) { System.out.println(e + " in " + Thread.currentThread().getName()); }// throw new NullPointerException(); } };

thread.start(); try { System.out.println("main will be join to " + thread.getName()); thread.join(); } catch (InterruptedException e) { System.out.println(e + " in main"); } System.out.println(thread.getName() + " is " + thread.getState()); System.out.println("main thread after join");

// throw new NullPointerException();

main will be join to Thread-0Thread-0 is runningThread-0 is TERMINATEDmain thread after join

throw new NullPointerException();

main will be join to Thread-0Thread-0 is runningException in thread "Thread-0" java.lang.NullPointerExceptionThread-0 is TERMINATED

at JoinTest$1.run(JoinTest.java:24)main thread after join

Page 8: Java concurrency   begining

Interruption

Interruption processing example:

if (Thread.interrupted()) {

// do some work if needed

throw new InterruptedException();

}

Interruption - is a signal for the thread that it should stop an execution.

Each thread has to handle his own interruption.

public void interrupt() - interrupts this thread.

scenarios: - clears intrerrupt status, throws InterruptedException; - sets intrerrupt status; - does nothing.

How to find out interrupt status?

static boolean interrupted() boolean isInterrupted()

clears interrupt status doesn't clear interrupt status

Page 9: Java concurrency   begining

Volatile There are several ways that can enhance performance:

• storing data in registers

• changing the order of operations

Example:

public void run( ) {

while (!done) {

foo( );

}

}

public void setDone( ) {

done = true;

}

private boolean done = false; private volatile boolean done = false;

Begin method runLoad register r1 with memory location 0Xff12345Label L1:Test if register r1 == 1If true branch to L2Call method fooBranch to L1Label L2:End method run

Begin method runLabel L1:Test if memory location 0xff12345 == 1If true branch to L2Call method fooBranch to L1Label L2:End method

done is read only from register - operations always will be looped

done is read from main memory - there is a chance to escape from loop

Keyword volatile guaranties that value is never stored to registers.

Synchronization has the similar effect because of registers' invalidation

Page 10: Java concurrency   begining

Changing operations' orderWe can not rely on operations' order to avoid costs of synchronization:

Example: public int currentScore, totalScore, finalScore

public void resetScore(boolean done) {

totalScore += currentScore;

if (done) {

finalScore = totalScore;

currentScore = 0;

}

}

public int getFinalScore( ) {

if (currentScore == 0)

return finalScore;

return -1;

}

finalScore = totalScore; currentScore = 0;

currentScore = 0; finalScore = totalScore;

Thread1: Update total score

Thread2: See if currentScore == 0

Thread2: Return -1

Thread1: Update finalScore

Thread1: Set currentScore == 0

Thread1: Update total score

Thread1: Set currentScore == 0

Thread2: See if currentScore == 0

Thread2: Return finalScore

Thread1: Update finalScore

logic is correct logic is corrupted

Synchronization protects operations from mixing:

JVM cannot move operation outside the synchronization block.

But operation that is located before or after synchronization block can be moved into synchronization block.

Page 11: Java concurrency   begining

Lock starvationLock starvation occurs when a particular thread attempts to acquire a lock and never succeeds because another thread is already

holding the lock.

Reasons:

The thread knows nothing about other contestants.

The thread with higher priority is served first.

A generous amount of threads.

Synchronized blocks within loops often have this problem:

while(true) {

synchronized (this) {

// execute some code

}

}

Page 12: Java concurrency   begining

ReentrantLockSolves lock starvation problem

Construct a ReentrantLock object where the fairness flag is set to true.

Threads are served very close to a first-come, first-served basis regardless of the thread's priority.

Drawbacks:

The priority can be set because the developer wanted to have the scheduler behave in a certain manner. ReentrantLock may change the desired scheduling behavior.

Example:

final ReentrantLock _lock = new ReentrantLock(true); @Overridepublic void run() { System.out.println(Thread.currentThread().getName() + " before sb"); _lock.lock(); // will wait until this thread gets the lock try { System.out.println(Thread.currentThread().getName() + " in sb"); finally { //releasing the lock so that other threads can get notifies _lock.unlock(); }}

fairness false fairness true

Thread-0 before sbThread-9 before sbThread-3 before sbThread-7 before sbThread-4 before sbThread-6 before sbThread-5 before sbThread-2 before sbThread-1 before sbThread-2 in sbThread-5 in sbThread-6 in sbThread-4 in sbThread-7 in sbThread-3 in sbThread-9 in sbThread-0 in sbThread-1 in sb

Thread-1 before sbThread-2 before sbThread-5 before sbThread-6 before sbThread-6 in sbThread-1 in sbThread-2 in sbThread-5 in sbThread-3 before sbThread-3 in sbThread-7 before sbThread-7 in sbThread-9 before sbThread-9 in sbThread-0 before sbThread-0 in sbThread-4 before sbThread-4 in sb

Page 13: Java concurrency   begining

Reader/Writer LocksReentrantReadWriteLock class.

Generally, reader/writer locks are used when there are many more readers than writers;

The reader/writer lock is needed—to share data access during the long periods of time when only reading is needed.

ReadLock - can be shared between readers

WriteLock - exclusive

work principle:

preconditions: there are lots of writers and readers;

behaviour (fairness = false) :

behaviour (fairness = true) :

Threads are served as first-come, first-served basis

Reader has finished task Writer will be called;

Writer has finished task Reader or Writer will be called (arbitrary schedule);

Page 14: Java concurrency   begining

ReentrantReadWriteLock example

static class RRWLC {

ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();

private final Lock read = readWriteLock.readLock();

private final Lock write = readWriteLock.writeLock();

StringBuilder sb = new StringBuilder();

public void read() {

read.lock();

try{ TimeUnit.SECONDS.sleep(2); //sout

} finally { read.unlock(); //sout }

}

public void write (){

write.lock();

try{

System.out.println("Now sb = " + sb.append(" + "));//sout

} finally{ write.unlock(); //sout }

}

}

static class Reader extends Thread {private RRWLC rrwlc; @Override public void run() { ... rrwlc.read(); ...}}

static class Writer extends Thread {private RRWLC rrwlc; @Override public void run() { ... rrwlc.write(); ...}}

Result:

Reader :Thread-7 is reading : sb = Reader :Thread-5 is reading : sb = Reader :Thread-0 is reading : sb = Reader :Thread-2 is reading : sb = Reader :Thread-3 is reading : sb = Reader :Thread-5 has finished readingReader :Thread-3 has finished readingReader :Thread-0 has finished readingReader :Thread-2 has finished readingReader :Thread-7 has finished readingWriter :Thread-8 is writing Now sb = + Writer :Thread-8 has finished writingWriter :Thread-9 is writing Now sb = + + Writer :Thread-9 has finished writingReader :Thread-1 is reading : sb = + + Reader :Thread-4 is reading : sb = + + Reader :Thread-1 has finished readingReader :Thread-4 has finished readingWriter :Thread-11 is writing Now sb = + + + Writer :Thread-11 has finished writingReader :Thread-6 is reading : sb = + + +Reader :Thread-6 has finished readingWriter :Thread-10 is writing Now sb = + + + + Writer :Thread-10 has finished writing

Page 15: Java concurrency   begining

Thread Local Variables

Thread local variable:- is private to particular thread;

- cannot be used to share state between threads;

- access to the variable need never be synchronized;

If TLV has never been set before the initialValue() is called to return the value.

Qualities Thread Local Local Object

state independency + +

Common monitor exists + -

Lazy initialization + -

disadvantages: Perfomance loses on chosing/getting object

Page 16: Java concurrency   begining

Thread Local examplepublic class TestTreadLocal

{

public static void main(String[] args) {

final ThreadLocal<MyObject> perThreadCounter = new ThreadLocal<MyObject>();

Thread t1 = new Thread() {

public void run() {

perThreadCounter.set(new MyObject(2));

info(perThreadCounter);

perThreadCounter.get().setX(8);

timeout(3);

info(perThreadCounter);

}

};

System.out.println("In main : " + perThreadCounter.get());

t1.start();

timeout(1);

t2.start();

}

Thread t2 = new Thread() {

public void run() {

perThreadCounter.set(new MyObject(3));

info(perThreadCounter);

perThreadCounter.get().setX(7);

timeout(3);

info(perThreadCounter);

}

};

Result:

In main : nullThread-0 : java.lang.ThreadLocal@4ce86da0 : concurrency.TestTreadLocal2$MyObject@2f754ad2 : 2Thread-1 : java.lang.ThreadLocal@4ce86da0 : concurrency.TestTreadLocal2$MyObject@95c7850 : 3Thread-1 : java.lang.ThreadLocal@4ce86da0 : concurrency.TestTreadLocal2$MyObject@95c7850 : 7Thread-0 : java.lang.ThreadLocal@4ce86da0 : concurrency.TestTreadLocal2$MyObject@2f754ad2 : 8

Page 17: Java concurrency   begining

Atomic operations and classesAllows operation that contains multiple instructions can be treated as atomic (for example ++) ;

Classes: AtomicInteger, AtomicLong, AtomicBoolean, and AtomicReference.

get() and set() - provide volatile functionality.

getAndSet() - sets the new value returns the previous value.

The AtomicInteger and AtomicLong additional methods:

incrementAndGet(), getAndIncrement() - pre/post-increment

decrementAndGet(), getAndDecrement() - pre/post-decrement

addAndGet(), getAndAdd() - pre- and post-operators for the delta value.

compareAndSet (exp, new)

weakCompareAndSet (exp, new)

compare exp with current value. if they are equal - set new value, return true.

Page 18: Java concurrency   begining

Atomic field updaters

AtomicIntegerFieldUpdater, AtomicLongFieldUpdater, AtomicReferenceFieldUpdater can help to update volatile variables.

These classes are abstract. Use the static newUpdater() method.

Features:

• Is used as "wrappers" around a volatile field;

• Don't require adding an extra object for atomic access to your class;

• Let refer to the variable "normally" (without get, set);

To avoid using the synchronization via the atomic classes we have to make:

• Simple variable substitution;

• Changing algorithms;

• Retrying operations;

Page 19: Java concurrency   begining

Implementation differences

synchronized AtomicInteger AtomicIntegerFieldUpdater

public class AtomicCounter {

int value = 0;

public synchronized void increment () {value++;}

publiclass AtomicCounter {

private final AtomicInteger value = new AtomicInteger(0);

public int increment () {return value.incrementAndGet();}

// or via compareAndSet

public void increment() { while (true) { int current = value.get(); int next = current + 1;

if (integer.compareAndSet(current, next)){ return; } } }

public class AtomicIntegerFieldUpdaterExample {

public static void main(String[] args) { Counter c1 = new Counter(10); Counter c2 = new Counter(20);

AtomicIntegerFieldUpdater<Counter> iUpdater = AtomicIntegerFieldUpdater.newUpdater(Counter.class, "value");

iUpdater.incrementAndGet(c1); iUpdater.incrementAndGet(c2); System.out.println("c1.value = " + iUpdater.get(c1)); System.out.println("c2.value = " + iUpdater.get(c2)); }

static class Counter{

volatile int value; // private is not allowed

Counter(int value) { this.value = value; } }}

Page 20: Java concurrency   begining

SemaphoreSemaphore is a lock with a counter.

Lets different threads acquire lock one or more times.

Features:

• A number of simultaneously executing threads have to be set in the constructor;

• The acquire() and release() are similar to the lock() and unlock() methods of the

Lock;

• Supply fairness politics.

• There is no concept of nesting;

Page 21: Java concurrency   begining

Semaphore example

static class SemaphoreTester extends Thread {

private Semaphore s; SemaphoreTester(Semaphore s) { this.s = s;} @Override public void run() { try { System.out.println(getName() + " at first : is trying to acquire the lock "); s.acquire(); System.out.println(getName() + " semaphore's first acquire has got"); System.out.println(getName() + " second time: is trying to acquire the lock " + TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis())); s.acquire(); System.out.println(getName() + " semaphore's second acquire " + TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis())); TimeUnit.SECONDS.sleep(5); s.release(); System.out.println(getName() + " semaphore has been released"); s.release(); System.out.println(getName() + " semaphore has been released"); } catch (InterruptedException e) { e.printStackTrace(); lates. }

} }}

public class SemaphoreExample { public static void main(String[] args) {

Semaphore sem = new Semaphore(3); SemaphoreTester st1 = new SemaphoreTester(sem); SemaphoreTester st2 = new SemaphoreTester(sem); st1.start(); st2.start(); }

Result:

Thread-1 at first : is trying to acquire the lock Thread-1 semaphore's first acquire has gotThread-1 second time: is trying to acquire the lock 1351581890Thread-1 semaphore's second acquire 1351581890Thread-0 at first : is trying to acquire the lock Thread-0 semaphore's first acquire has gotThread-0 second time: is trying to acquire the lock 1351581890Thread-1 semaphore has been releasedThread-1 semaphore has been releasedThread-0 semaphore's second acquire 1351581895Thread-0 semaphore has been releasedThread-0 semaphore has been released

Page 22: Java concurrency   begining

BarrierThe barrier is simply a waiting point where all the threads can sync up.

The last thread releases the barrier by notifying all of the other threads.

Barrier against join()

benefits:

we mustn't always create new threads;

logical operations can be placed together;

drawbacks:

complex reinitialization process;

Barrier can be broken when:

1. Waiting threads can be interrupted;

2. Thread may break through the barrier due to a timeout condition;

3. Exception could be thrown by the barrier action.

stage 1 waiting stage 2 stage 3 ...waiting

stage 1 waiting stage 2 stage 3 ...

stage 1 waiting

stage 2 stage 3 ...waiting

waiting

Page 23: Java concurrency   begining

CyclicBarrier examplepublic class CyclicBarrierExample

{

private static int matrix[][] =

{{ 1 }, { 2, 2 }, { 3, 3, 3 }, { 4, 4, 4, 4 }, { 5, 5, 5, 5, 5 }};

private static int results[];

public static void main(String args[])

{

final int rows = matrix.length;

results = new int[rows];

Runnable merger = new Runnable(){

public void run()

{ int sum = 0;

for (int i = 0; i < rows; i++)

sum += results[i];

System.out.println("Merger :"+ " Results are: " + sum); }};

CyclicBarrier barrier = new CyclicBarrier(rows, merger);

for (int i = 0; i < rows; i++)

{ new Summer(barrier, i).start(); }

}

private static class Summer extends Thread {

int row;

CyclicBarrier barrier;

Summer(CyclicBarrier barrier, int row)

{ this.barrier = barrier;

this.row = row; }

public void run() {

int columns = matrix[row].length;

int sum = 0;

for (int i = 0; i < columns; i++)

sum += matrix[row][i];

results[row] = sum;

System.out.println(Thread.currentThread().getName() + ": Results for row " + row + " are : " + sum);

try{

barrier.await(); // wait for others

System.out.println(Thread.currentThread().getName() +

" do something else " + System.currentTimeMillis());

} catch ...

Page 24: Java concurrency   begining

Result:

Thread-0: Results for row 0 are : 1Thread-1: Results for row 1 are : 4Thread-2: Results for row 2 are : 9Thread-4: Results for row 4 are : 25Thread-3: Results for row 3 are : 16Merger : Results are: 55Thread-3 do something else 1351699717125Thread-0 do something else 1351699717125Thread-1 do something else 1351699717125Thread-2 do something else 1351699717125Thread-4 do something else 1351699717125

Page 25: Java concurrency   begining

The end

Page 26: Java concurrency   begining

References

Books: Java Threads, Third Edition; Scott Oaks & Henry Wong; ISBN: 978-0-596-00782-9. O'Reilly, 2004.

Concurrent Programming in Java: Design Principles and Patterns (2nd edition) Doug Lea; ISBN 0-201-31009-0. AddisonWesley, 1999.

Web recources:http://www.javamex.com/tutorials/synchronization_concurrency_7_atomic_updaters.shtml

http://www.baptiste-wicht.com/2010/09/java-concurrency-atomic-variables/

http://www.ibm.com/developerworks/java/library/j-jtp11234/

http://programmingexamples.wikidot.com/cyclicbarrier

http://javarevisited.blogspot.com/2012/07/cyclicbarrier-example-java-5-concurrency-tutorial.html

http://tutorials.jenkov.com/java-concurrency/semaphores.html

http://alexbtw.livejournal.com/3355.html