java threads: synchronization

24
1 Java threads: synchronization

Upload: lee-humphrey

Post on 30-Dec-2015

48 views

Category:

Documents


0 download

DESCRIPTION

Java threads: synchronization. Thread states. New : created with the new operator (not yet started ) Runnable : either running or ready to run Blocked : deactivated to wait for something Dead : has executed its run method to completion, or terminated by an uncaught exception - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: Java threads: synchronization

1

Java threads: synchronization

Page 2: Java threads: synchronization

2

Thread states

1. New: • created with the new operator (not yet started)

2. Runnable: • either running or ready to run

3. Blocked: • deactivated to wait for something

4. Dead: • has executed its run method to completion, or

terminated by an uncaught exception

• a started thread is executed in its own run-time context consisting of: program counter, call stack, and some working memory (registers, cache)

Page 3: Java threads: synchronization

3

Thread states

Page 4: Java threads: synchronization

4

Thread states (cont.)

• don't confuse the interface java.lang.Runnable with the state Runnable

• "running" is not a separate state within Runnable, but

Thread.currentThread () // static method

identifies the current thread object (for itself)

• thread.isAlive () tells if the thread has been started (is not New) and not yet Dead – you cannot directly ask whether an alive thread is

Runnable or Blocked – you cannot directly ask whether a non-alive thread

is still New or already Dead

Page 5: Java threads: synchronization

5

Entering the Blocked state

A thread enters the blocked state when:

1. the thread goes to sleep by calling the sleep method

2. the thread calls an operation that blocks until IO operations are complete

3. the thread tries to acquire a lock that is currently held by another thread (mutual exclusion)

4. the thread waits for a condition

Page 6: Java threads: synchronization

6

Transition from Blocked to Runnable

A blocked thread moves into the Runnable state when

1. if in sleep, the specified number of milliseconds have been expired

2. if waiting for the completion of an IO operation, the IO operation have finished

3. if waiting for a lock that was owned by another thread, the other thread have released its ownership of the lock

4. if waits for a condition, another thread signals that the condition (may) have changed

– additionally, can wait for a lock or a condition with a timeout

Page 7: Java threads: synchronization

7

Java Memory Model• computers can temporarily hold memory values in

registers or local memory caches, and the compiler can reorder instructions to optimize throughput– without synchronization, threads (interleaved or

parallel) may see out-of-date and/or out-of-order values for the same memory locations

• locks always ensure that all calculated results are visible to other threads (flushing of caches, etc.)

• alternatively, you can declare a flag field as volatile:

private volatile boolean done; . .

public boolean isDone () { return done; } • a write to a volatile variable synchronizes with all

subsequent reads of that same variable (all reads and writes are atomic, even for long and double)

Page 8: Java threads: synchronization

8

Mutual exclusion from critical code• basic way to protecting a shared code block:

myLock.lock (); // a ReentrantLock object

try {

critical section

} finally {

myLock.unlock ();

}• only one thread at a time can enter the critical

section (identified and protected by this particular lock)

• any other threads calling on this lock, are blocked until the first thread unlocks

• the finally clause makes sure the lock is unlocked even if an exception is thrown

Page 9: Java threads: synchronization

9

Fairness

• you can specify that you want a fair locking policy:

Lock fairLock = new ReentrantLock (true);

• a fair lock favors the thread that has been waiting for the longest time

• by default, locks are not required to be fair – for implementation reasons fair locks can be

much slower than regular locks– anyway, you have no guarantee that the thread

scheduler provided by the plarform is fair• so if the thread scheduler neglects a thread,

it may not get the chance to be treated fairly by the lock

Page 10: Java threads: synchronization

10

Condition objects

• a lock protects sections of code, allowing only one thread to execute the code at a time– a lock manages threads that are trying to enter a

protected code segment

• a lock can have one or more associated condition objects– each condition object manages threads that have

entered a protected code section but that cannot proceed for some reason (usually, lack of data / resource)

Page 11: Java threads: synchronization

11

Condition objects (cont.)

• use a condition object to manage threads that have acquired a lock but cannot proceed with useful work

Condition cond = lock.newCondition (); . .

lock.lock ();

try {

while (!(ok to proceed)) // try until succeeds

cond.await ();

// data available: do something useful

} finally {

lock.unlock ();

}• must itself check that the condition is (still) valid

Page 12: Java threads: synchronization

12

Condition objects (cont.)

• some other thread must call the signalAll method to wake up any waiting threads

change some item used in condition test . .

cond.signalAll (); // or rarely: signal ()

• the condition wait must always be done in a loop

while (! condition) cond.await ();

• multiple threads may compete to acquire the lock • the first one can use up resources, and so others

must then enter back to the blocked state to wait• additionally, a "spurious wakeup" (without signal) is

permitted to occur (depending on the OS)

Page 13: Java threads: synchronization

13

Keyword "synchronized"

• since version 1.0, every object has an implicit lock to be used in so-called monitor methods:

public synchronized void method () { body }

this is the equivalent of the following pseudocode:

public void method () { // use explicit lock

this.implicitLock.lock (); // pseudocode

try {

body

} finally {

this.implicitLock.unlock();

}

}

Page 14: Java threads: synchronization

14

Keyword synchronized (cont.)

• the monitor lock also has a single implicit associated condition with the following operations– wait adds a thread to its set of waiting threads – notifyAll unblocks all waiting threads to compete

for access• these correspond to the following lock calls

– implicitCondition.await (); // pseudocode– implicitCondition.signalAll ();

• the wait and notifyAll methods are final methods in Object – so the Condition methods had to be renamed

differently: await and signalAll

Page 15: Java threads: synchronization

15

Synchronized code blocks

• an implicit lock can be acquired also by entering a code block synchronized by an object lock:

synchronized (obj) { // any object

critical section // implicit lock and unlock

}

• the lock is reentrant: if a thread has acquired the lock, it can acquire it again (increments a count)

• similarly, a monitor method can call other monitor methods with the same implicit lock without waiting

• you can declare a static monitor method: it uses the the associated class object ("X.class") as its lock

Page 16: Java threads: synchronization

16

Limitations of implicit locks and conditions

• you cannot interrupt a thread that is waiting to acquire a lock

• you cannot specify a timeout when trying to acquire a lock

• having only one single condition per lock can be inefficient– cannot separate buffer empty (cannot take) vs.

buffer full (cannot put) conditions

• the virtual machine locking primitives do not map well to the most efficient locking mechanisms available in modern hardware

Page 17: Java threads: synchronization

17

Lock testing and timeouts

• can be cautious about acquiring a lock:

if (myLock.tryLock ()) {

// now the thread owns the lock

try { do something }

finally { myLock.unlock (); }

} else {

do something else }

• can call tryLock with a timeout parameter:

if (myLock.tryLock(10,TimeUnit.MILLISECONDS))

. . .– TimeUnit can be: SECONDS, MILLISECONDS,

MICROSECONDS, or NANOSECONDS.

Page 18: Java threads: synchronization

18

Lock testing and timeouts (cont.)

• if you call tryLock with a timeout, then it can throw InterruptedException when interrupted– can try to break up deadlocks and such– the lockInterruptibly method has the same

meaning as tryLock with an infinite timeout

• can also supply a timeout for a condition wait;

myCondition.await (10, TimeUnit.MILLISECONDS));

– returns if another thread has called signalAll - or if the given timeout has elapsed

– await methods throw an InterruptedException if the thread is interrupted (but awaitUninterruptibly)

Page 19: Java threads: synchronization

19

Summary of synchronization operations

• synchronized (lock) { . . use shared data } • synchronized void method () { . . use shared data } • lock.lock () { . . } finally { lock.unlock (); } // explicit

• waiting for a condition to proceed:

try { . .

while (!(cannot proceed))

wait (); . . // means: this.wait ();

} catch (InterruptedException e) { . . 

• either anyObject.wait (); or condition.await ();• either anyObject.notifyAll (); or condition.signalAll

();

Page 20: Java threads: synchronization

20

Recommendations

1. it is best to use neither lock/condition nor the old monitor methods/code blocks ("synchronized")• in many situations, better to use mechanisms of

the java.util.concurrent package that do all the locking and synchronization for you

• for example, a blocking queue can synchronize threads that work on a common pipeline

2. if the monitor methods work for your situation, prefer using them: • less code to write and so less room for error

3. use explicit locks/conditions only if you need the additional power of these constructs

Page 21: Java threads: synchronization

21

java.util.concurrent: thread management tools• ReentrantReadWriteLock provides shared access for readers,

and exclusive access for a writer• BlockingQueue <E> can cause a thread to block when trying to

add to a full queue or trying to remove from empty one– ConcurrentLinkedQueue <E> and ArrayBlockingQueue <E>

provide optionally-bounded/bounded thread-safe queues • interface ConcurrentMap <K,V> implemented efficiently by

– ConcurrentHashMap and ConcurrentSkipListMap • CopyOnWriteArrayList <E> and CopyOnWriteArraySet <E>

provide thread-safe collections (reads outnumber mutations)• Callable <T> plus Future <T> can return and hold the result of

an asynchronous computation (can test, time-out, and cancel) • you can give a Runnable to a thread pool, and one of its idle

threads executes it; afterwards, that thread can serve again• synchronizers: CyclicBarrier, CountDownLatch, Exchanger,

SynchronousQueue, and Semaphore • AtomicInteger, AtomicLong, AtomicReference, etc.: lock-free

thread-safe programming on single variables

Page 22: Java threads: synchronization

22

Atomic variables• support lock-free thread-safe programming on single

variables (objects)– extends the notion of volatile: provide atomic and

synchronized access to single values (visibility!) • enable implementations to use efficient atomic

machine instructions that available on processors (but no guarantees: might entail internal locking ..)

AtomicLong seqNo = new AtomicLong (0); . . long next () { return seqNo.getAndAdd(1); } // atomic– may not work when state involves invariants..

• atomic variables do not replace Integer, Long, etc. – do not provide hashCode or compareTo (since

expected to change, no good as Map keys)

Page 23: Java threads: synchronization

23

A producer-consumer solution

class IntProducer implements Runnable {

private BlockingQueue <Integer> queue;

public IntProducer (BlockingQueue <Integer> q) {

queue = q; new Thread (this).start ();

}

public void run () {

try { // "put" may block

for (int x = 0; x < 100; x++) {

Thread.sleep (delay); . . queue.put (x);

}

} catch (InterruptedException e) { }

}

}

Page 24: Java threads: synchronization

24

A producer-consumer solution (cont.)

class IntConsumer implements Runnable {

private BlockingQueue <Integer> queue;

public IntConsumer (BlockingQueue <Integer> q) {

queue = q; new Thread (this).start ();

}

public void run () {

try { // "take" may block

while (true) { int value = queue.take (); . . }

} catch (InterruptedException ex) { }

} } . .

BlockingQueue <Integer> q = new ArrayBlockingQueue <Integer> (10); // capacity