concurrency in jdk 5.0 cit concurrency · cit concurrency java implicit concurrency (3) •j dk 5.0...
TRANSCRIPT
CIT
Con
curr
ency Concurrency in JDK 5.0
Thread-safe collections, Task Management, Synchronizers, Low-
level Facilities
CIT
Con
curr
ency
Java Implicit Concurrency (1)• AWT and Swing
– Background event thread– Event Listener classes must be thread-safe
• TimeTask – scheduling and periodic execution of tasks– TimerTask events execute in the Timer thread– TimerTasks must be thread safe
• Servlets and JavaServer Pages– Servlet containers create multiple threads and may call a
given servlet concurrently from multiple threads for multiple requests
– Servlets classes must be thread safe• RMI
– Remote classes must be thread safe
CIT
Con
curr
ency
Java Implicit Concurrency (2)• Thread safe class
– Must behave correctly in a single-threaded environment• No sequence of operations (public interface) puts objects in an
invalid state, • Or observe the object to be in an invalid state• Or violate any of the class’s invariants, pre- and post-
conditions
– Must continue to behave correctly when accessed from multiple threads
• Regardless of the scheduling or interleaving of the execution ofthose threads by the runtime environment
• Without any additional synchronisation on the part of the calling code
CIT
Con
curr
ency
Java Implicit Concurrency (3)• JDK 5.0
– java.util.concurrent– JVM-level changes (exploitation of hardware
level concurrency support – compare-and-swap instruction)
– Low-level utility classes – locks and atomic variables
– High-level utility classes – OS constructs (e.g. semaphores, mutexes, barriers), thread pools and thread-safe collection classes
CIT
Con
curr
ency
Thread-safe collections (1)• Collections
– Some are already thread safe: Hashtable and Vector– The rest can be made thread safe: syncrhonised wrapper
factories Collections.synchronisedList/Map/Set– Need to hold a lock while iterating!
• Fail-fast iterators• ConcurrentModificationException – conditional thread-safety
• java.util.concurrent– ConcurrentHashMap, CopyOnWriteArrayList,
CoppyOnWriteArraySet, Queue, BlockingQueue• Weakly consistent iterators
– A removed item not already returned will not be returned– An added item after the start of the iteration may or may not be
returned– No element will be returned twice in a single iteration
CIT
Con
curr
ency
Thread-safe collections (2)• CopyOnWriteArrayList/Set
– A new copy is created whenever an element is added or removed
– Avoids locking during iteration– Useful for list of listeners
• ConcurrentHashMap– Cannot be locked for exclusive use, but provides atomic
methods for common compound operations, e.g. put-if-absent
– Returns weakly consistent iterators• Queue interface – FIFO and priority classes• BlockingQueue – bounded or unbounded
– Producer/Consumer
CIT
Con
curr
ency
Bounded Buffer (1)• A bounded buffer consists of a fixed number of
slots– Items are put into the buffer by a producer process and
removed by a consumer process – It can be used to smooth out transfer rates between the
producer and consumer
CIT
Con
curr
ency
Bounded Buffer (2)public interface Buffer {…}
class BufferImpl implements Buffer {…
public synchronized void put(Object o) throws InterruptedException {
while (count==size) wait();buf[in] = o; ++count; in=(in+1)%size;notify();
}public synchronized Object get()
throws InterruptedException {while (count==0) wait();Object o =buf[out]; buf[out]=null; --count; out=(out+1)%size;notify();return (o);}
}
We separate the interface to permit an alternative implementation later.
CIT
Con
curr
ency
Bounded Buffer (3)class Producer implements Runnable {
Buffer buf;String alphabet= "abcdefghijklmnopqrstuvwxyz";
Producer(Buffer b) {buf = b;}
public void run() {try {
int ai = 0;while(true) {
ThreadPanel.rotate(12);buf.put(new Character(alphabet.charAt(ai)));ai=(ai+1) % alphabet.length();ThreadPanel.rotate(348);
}} catch (InterruptedException e){}
}}
Similarly Consumer which calls buf.get()
CIT
Con
curr
ency
Bounded Buffer (4)• The same in java 5.0
– The buffer
– The consumer
– The producer
LinkedBlockingQueue<Character> buf = new LinkedBlockingQueue<Character>(n);
buf.put(new Character(alphabet.charAt(ai)));
Character c = buf.take();
CIT
Con
curr
ency
Task Management (1)• Executor interface
– Objects that run Runnables– Separate task submission from task execution policy– Various threading options: background thread, thread
pool, in the calling thread (pass-through), new thread (thread-per-request), in another JVM
– Instantiation through factory methods• Executors.newCachedThreadPool()• Executors.newFixedThreadPool(int n)• Executors.newSingleThreadExecutor()
• ExecutorService interface– Extension of executor with lifecycle management of
execution service
CIT
Con
curr
ency
Task Management (2)class ReliableWebServer {
Executor pool = Executors.newFixedThreadPool(7);public static void main(String[] args) {
ServerSocket socket = new ServerSocket(80);while (true) {
final Socket connection = socket.accept();Runnable r = new Runnable() {
public void run() {handleRequest(connection);
}};pool.execute(r);
}}
}
CIT
Con
curr
ency
Task Management (3)• Executor customisation through custom
ThreadFactory• In case of execution inability –
RejectedExecutionHandler– Throw exception– Discard task– Execute in caller’s thread– Discard old task in queue
• Custom executors with overidden beforeExecuteand afterExecute– Instrumentation, logging, timing, reinitialisation of
thread-local variables, execution customisations
CIT
Con
curr
ency
Task Management (4)• Callable: result bearing Runnable• Future interface
– FutureTask – implements interface, can be submitted to an Executor
• ExecutorService.submit() – returns a Future interface
– Future.get() – retrieves results or throws ExecutionException – it is blocking
• CompletionService interface– Decouple results processing from task
execution, e.g. producer/consumer– ExecutorCompletionInterface
CIT
Con
curr
ency
Task Management (5)public class Cache<K, V> {
ConcurrentMap<K, FutureTask<V>> map = new ConcurrentHashMap();
Executor executor = Executors.newFixedThreadPool(8);public V get(final K key) {
FutureTask<V> f = map.get(key);if (f == null) {
Callable<V> c = new Callable<V>() {public V call() {
// return value associated with key}
};f = new FutureTask<V>(c);FutureTask old = map.putIfAbsent(key, f);if (old == null) executor.execute(f);else f = old;
}return f.get();
}}
CIT
Con
curr
ency
Semaphores (1)• Semaphore s is an
integer variable that can take only non-negative values– Semaphores are widely
used for dealing with inter-process synchronization in operating systems
• The only operations permitted on s are up(s) and down(s)– Blocked processes are
held in a FIFO queue.
down(s): if s >0 thendecrement s
elseblock execution of the calling process
up(s): if processes blocked on s thenawaken one of them
elseincrement s
CIT
Con
curr
ency
Semaphores (2)
public class Semaphore {
private int value;
public Semaphore (int initial) { value = initial;}
synchronized public void up() {++value;notify();
}
synchronized public void down()throws InterruptedException {
while (value==0) wait();--value;
}}
CIT
Con
curr
ency
Bounded Buffer (2)public interface Buffer {…}
class BufferImpl implements Buffer {…
public synchronized void put(Object o) throws InterruptedException {
while (count==size) wait();buf[in] = o; ++count; in=(in+1)%size;notify();
}public synchronized Object get()
throws InterruptedException {while (count==0) wait();Object o =buf[out]; buf[out]=null; --count; out=(out+1)%size;notify();return (o);}
}
We separate the interface to permit an alternative implementation later.
CIT
Con
curr
ency
Nested Monitors (1)• Suppose that, in place of using the count variable
and condition synchronization directly, we instead use two semaphores full and empty to reflect the state of the buffer. class SemaBuffer implements Buffer {
…
Semaphore full; //counts number of itemsSemaphore empty; //counts number of spaces
SemaBuffer(int size) {this.size = size; buf = new Object[size];full = new Semaphore(0);empty= new Semaphore(size);
}…}
CIT
Con
curr
ency
Nested Monitors (2)
• Empty is decremented during a put operation, which is blocked if empty is zero
• Full is decremented by a get operation, which is blocked if full is zero.
synchronized public void put(Object o) throws InterruptedException {
empty.down();buf[in] = o;++count; in=(in+1)%size;full.up();
}synchronized public Object get()
throws InterruptedException{full.down();Object o =buf[out]; buf[out]=null;--count; out=(out+1)%size;empty.up();return (o);
}
Does this behave as desired?
CIT
Con
curr
ency
Nested Monitors (3)• The only way to avoid nested monitor deadlock in
Java is by careful design– In this example, the deadlock can be removed by
ensuring that the monitor lock for the buffer is not acquired until after semaphores are decremented.
public void put(Object o) throws InterruptedException {
empty.down();synchronized(this){buf[in] = o; ++count; in=(in+1)%size;
}full.up();
}
CIT
Con
curr
ency
Syncrhonizer Classes• Coordination and control of execution flow of threads• Semaphore
– Thread acquire any number of permits– Mutex – mutual exclusion semaphore or binary semaphore
• Similar to locks but allow release from different thread – useful in deadlock recovery
• CyclicBarrier– Reusable barrier for a group of threads– Maybe used with a timeout
• CountdownLatch– Similar to barrier but not reusable– Separates barrier arrival from waiting– Can be used as a starting gate
• Exchanger– Rendezvous– Similar to a barrier of two but with data exchange
CIT
Con
curr
ency
Readers/Writers (1)• A shared database is accessed by two kinds of
processes. Readers execute transactions that examine the database while Writers both examine and update the database. A Writer must have exclusive access to the database; any number of Readers may concurrently access it.
Light blue indicates database access.
CIT
Con
curr
ency
Readers/Writers (2)• We concentrate on the monitor implementation• We define an interface that identifies the monitor
methods that must be implemented, and develop a number of alternative implementations of this interface. – Firstly, the safe READWRITELOCK.
interface ReadWrite {public void acquireRead()
throws InterruptedException;public void releaseRead();public void acquireWrite()
throws InterruptedException;public void releaseWrite();
}
CIT
Con
curr
ency
Readers/Writers (3)class ReadWriteSafe implements ReadWrite {
private int readers =0;private boolean writing = false;
public synchronized void acquireRead()throws InterruptedException {
while (writing) wait();++readers;
}
public synchronized void releaseRead() {--readers;if(readers==0) notify();
}
Unblock a single writer when no more readers.
CIT
Con
curr
ency
Readers/Writers (4)public synchronized void acquireWrite()
throws InterruptedException {while (readers>0 || writing) wait();writing = true;
}
public synchronized void releaseWrite() {writing = false;notifyAll();
}}
Unblock all readers
This monitor implementation suffers from the WRITE progress problem: possible writer starvation if the number of readersnever drops to zero.
Strategy: Block readers if there is a writer waiting.
CIT
Con
curr
ency
Readers/Writers (5)class ReadWritePriority implements ReadWrite{
private int readers =0;private boolean writing = false;private int waitingW = 0; // no of waiting Writers.public synchronized void acquireRead()
throws InterruptedException { while (writing || waitingW>0) wait();
++readers; }public synchronized void releaseRead() { --readers; if (readers==0) notify(); }synchronized public void acquireWrite() { ++waitingW;
while (readers>0 || writing) try{ wait();} catch(InterruptedException e){}
--waitingW; writing = true; }synchronized public void releaseWrite() { writing = false; notifyAll(); }
}
Both READand WRITEprogress properties can be satisfied by introducing a turnvariable as in the Single Lane Bridge.
CIT
Con
curr
ency
Low-Level facilities• Locks
– Lock interface– Like the syncrhonized lock but with variations
• Timed waits, interruptible waits, lock polling, multiple condition wait sets per lock, non-blocking structured locking
– Reentrant lock implements interface• More scalable than synchronised• But, requires explicit unlocking
– Condition interface • Generalisation of wait()/notify() • Multiple conditions on the same lock
– ReadWriteLock– Can be fair, but fairness doesn’t scale
• Atomic variables– AtomicInteger/Long/Boolean/etc.