Java 5 concurrency


Published on

Published in: Technology, Business
1 Like
  • Be the first to comment

No Downloads
Total views
On SlideShare
From Embeds
Number of Embeds
Embeds 0
No embeds

No notes for slide

Java 5 concurrency

  1. 1. Java 5 Concurrency1.1 LocksBefore Java 5 concurrency was achieved using synchronized locks andwait/notify idiom. Synchronization is a locking mechanism where a block ofcode or method is protected by a software lock. Any thread that wants toexecute this block of code must first acquire the lock. The lock is releasedonce the thread exits the synchronization block or method. Acquiring of lockand releasing it is done by compiler thus relieving programmer of lock book-keeping. However, there are some draw backs while using synchronization asyou will see below.Wait/Notify idiom allows a thread to wait for a signal from other thread.Wait can be timed or can be interrupted (using Thread.interrupt()).Wait is signaled using notify.1.1.1 Drawbacks of Synchronization No Back-off: Once a thread enters a synchronization block or method, it has to wait till lock is available. It cannot back off to execute other instructions if lock is not available or it is taking very long time to get the lock. No Read-Only Access: Multiple threads cannot acquire lock even if read-only access is required. Compile Time: Code synchronization is compile time decision. Synchronization cannot be turned off because of run-time conditions. To enable this, a lot of code duplication is required. No Metadata: Lock metadata like number of threads waiting for lock, average time to acquire lock is not available in java program.1.1.2 Lock InterfaceAs of Java 5, Lock interface implementations can be used instead ofsynchronization.When a thread creates lock object, memory synchronization with cache occurs.This behavior is similar to entering a synchronized block or method.Lock interface has methods to lock and lock interruptibly.
  2. 2. Lock Interruptibly: This method acquires the lock unless the thread is interruptedby another thread. On calling this method, if lock is available it isacquired. If lock is not available, the thread becomes dormant and waits forlock to be available. If some other thread calls interrupt on this thread,then interrupted exception is thrown.tryLock: This methods immediately acquires the lock and returns true if lock isavailable. If lock is not available, it returns false.1.1.3 Lock Implementation1.1.3.1 ReentrantLockReentrant Lock is an implementation of Lock Interface. It allows a thread tore-enter the code that is protected by this lock object.It has additional methods that return the state of the lock and other metainformation.Reentrant Lock can be created with fair parameter. Lock is then acquired bythread in arrival order. ReentrantReadWriteLockThis is an implementation of ReadWriteLock. It hold a pair of associatedlocks one for read only operations and other for write only operations.The corresponding locks are readlock and writelock. Read locks can be sharedby multiple readers. Write lock is exclusive, i.e., write lock can be grantedto only one writer thread when no reader thread has a read lock.A reader thread is one that performs a read operation. A writer threadperforms write operations.When fair is true, the locks are granted based on arrival order of threads.Lock DowngradingLock downgrading is allowed, i.e., if a thread holds write lock, it can thenhold a read lock and then release write lockReentrantReadWriteLock l = ne ReentrantReadWriteLock();l.writelock().lock();l.readLock.lock();l.writelock.unlock();
  3. 3. Lock UpgradingLock Upgrading is not allowed, ie. if a thread holds read lock then it cannothold write lock without releasing read lock.RenetrantReadWriteLock l = new ReentrantReadWriteLock();l.readLock().lock();//process..l.readLock.unlock();// first unlock then acquire write lockl.writeLock().lock();Concurrency ImprovementWhen there are large numbers of reader threads and small number of writerthreads, readwritelock will improve concurrency.1.1.4 Typical Lock Usagepublic class LockedMap { private Lock l = new ReentrantLock(); Map myMap = new HashMap(); public Object get(Object key){ l.lock(); try { return myMap.get(key); } finally { l.unlock(); } } public void put(Object key, Object val) { l.lock(); try{ myMap.put(key,val); }finally{ l.unlock(); } }}
  4. 4. 1.2 ConditionCondition interface factors out object monitor methods wait, notify,notifyall into separate class. Condition objects are intrinsically bound to alock and can be obtained by calling newCondition() on lock instance.When lock replaces synchronized methods and blocks, conditions replace objectmonitor methods.Conditions are also called condition variables or condition queues.On the same lock object multiple condition variables can be created.Different set of threads can wait on different condition variable. A classicusage is in producer and consumers of a bounded buffer.public class BoundedBuffer { final Object[] buffer = new Object[10]; Lock l = new ReentrantLock(); Condition producer = l.newCondition(); Condition consumer = l.newCondition(); int bufferCount, putIdx, getIdx; public void put(Object x) throws InterruptedException{ l.lock(); try { while (bufferCount == buffer.length) producer.await(); buffer[putIdx++] = x; if (putIdx == buffer.length) putIdx = 0; ++bufferCount; consumer.signal(); } finally { l.unlock(); } } public Object get() throws InterruptedException { l.lock(); try{ while (bufferCount==0) consumer.await();
  5. 5. Object x = buffer[getIdx++]; if(getIdx==buffer.length) getIdx=0; --bufferCount; producer.signal(); return x; }finally{ l.unlock(); } }}Await UnInterruptiblyThis method on condition variable causes the thread to wait until a signal isexecuted on that variable.IllegalMonitorStateExceptionThe thread calling methods on condition variable should hold thecorresponding lock. If it doesn’t then illegalmonitorstateexception isthrown.1.3 Atomic VariablesAtomic variables are used for lock free, thread safe programming on singlevariables.As case with volatile variables, atomic variables are never cached locally.They are always synced with main memory.CompareAndSetAtomic variables use compare and swap (CAS) primitive of processors. CAS hasthree operands a memory location (V), expected old value of memory location(A), new value of memory location (B). If current value of memory locationmatches the expected old value(A), then new value (B) is written to memorylocation (V) and true is returned. In case the current value is differentfrom expected old value, then memory is not updated and false is return.Code logic can retry this operation if false is returned.
  6. 6. Below code shows CAS algorithm. However, the actual implementation is inhardware for processors that support CAS. For processors that do not supportCAS, locking as shown below is done to simulate CAS.public class SimulatedCAS {private int value;public synchronized int getValue() { return value; }boolean synchronized comapreAndSet(expectedVaue, updateValue) { bool set=false;if(value==expectedvalue) { value=newvalue; set=true;}else {set=false}return set;}}1.4 Data Structures1.4.1 Blocking QueueIt is a queue data structure with additional features like consumers of queuewait/block when queue is empty and producers wait/block when queue is full.Queue implementations can guarantee fairness where in longest waitingconsumer/producer get the first chance to access the queue.Below code depicts producer, consumer using blocking queue public class Producer implements Runnable { private final BlockingQueue q; public Producer(BlockingQueue q) { super(); this.q = q; }
  7. 7. @Override public void run() { try { while(true) { q.put(produce()); } } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } private Object produce() { // TODO Auto-generated method stub return new Object(); }}public class Consumer implements Runnable { private final BlockingQueue q; public Consumer(BlockingQueue q) { super(); this.q = q; } @Override public void run() { try { while(true) { consume(q.take()); } } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } private void consume(Object take) { // TODO Auto-generated method stub }}
  8. 8. public class Setup { /** * @param args */ public static void main(String[] args) { BlockingQueue q = new ArrayBlockingQueue(10,true); Producer p = new Producer(q); new Thread(p).start(); new Thread(new Consumer(q)).start(); new Thread(new Consumer(q)).start(); }}1.4.2 ConcurrentHashMapConcurrent Has Map is a thread safe hash map but does not block all get andput operations as synchronized version of hash map does. It allows fullconcurrency of gets and expected concurrency of puts.Concurrent Hash Map internally divides its storage in bins. The entries inbin are connected by link list.Nulls are not allowed in key and value.get operation generally does not entail locking. But algorithm checks ifreturn value is null the bin (segment) is first locked and then the value isfetched. A value can be null because of compiler reordering of instructions.put operations are performed by locking that particular bin (segment).1.5 SynchronizersSynchronizers control the flow of execution in one or more threads.1.5.1 SemaphoreA counting semaphore is used to restrict number of threads that can access aphysical or logical resource. Semaphore maintains a set of permits. Each callto acquire consumes a permit, possibly blocking if permit is not available.Each call to release(), releases a permit also signals a waiting acquirer.
  9. 9. Usage:A library has N seats and thus allows only N members at one time to use it.If all seats are occupied, then arriving members wait for the seat to getvacant. Design a model for the library.package com.concur.semaphore;import java.util.concurrent.Semaphore;public class Library { private final Semaphore s = new Semaphore(50, true); public void enter() throws InterruptedException { s.acquire(); } public void exit() throws InterruptedException { s.release(); } public void borrowBooks(int id){ //implementation } public void returnBook(int id){ //implementaion } public static void main(String args) { Library l = new Library(); try { l.enter(); l.borrowBooks(1234); l.exit(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } }}1.5.2 MutexMutex is a counting semaphore with only one permit. They have lot in commonwith locks. The difference is that in mutex some other thread can call arelease than the one holding the permit.
  10. 10. 1.5.3 Cyclic BarrierIn cyclic barrier, each threads come to a barrier and wait there till allthreads have reached the barrier. Once all threads reach barrier, they arereleased for further processing. Optionally a method can be called beforethreads are released.package com.concur.cyclic;import java.util.concurrent.BrokenBarrierException;import java.util.concurrent.CyclicBarrier;public class Barrier { final int num_threads; final CyclicBarrier cb; final boolean complete=false; public Barrier(int n) { num_threads=n; cb= new CyclicBarrier(num_threads, new Runnable(){ @Override public void run() { System.out.println("All threads reached barrier"); //check if complete and set complete }}); } public void process() throws InterruptedException, BrokenBarrierException { while(!complete) { //process cb.await(); //exits if process completed else loops } }}1.5.4 Countdown LatchCount down latch is similar to cyclic barrier but differs in way the treadsare released. In cyclic barrier, threads are released automatically when allthreads reach barrier. In countdown latch that is initialized by N, threadsare released when countdown has been called N times. Any call to await blockthreads if N!=0. Once N reaches 0, await() returns immediately.
  11. 11. Countdown latches cannot be reused. Once N reaches 0, all await() returnimmediately.package com.concur.countdown;import java.util.concurrent.CountDownLatch;public class Latch { private class Worker implements Runnable { final CountDownLatch start, done; public Worker(CountDownLatch start, CountDownLatch done) { this.start = start; this.done = done; // TODO Auto-generated constructor stub } @Override public void run() { try { start.await(); // do process done.countDown(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public static void main(String[] args) { Latch l= new Latch(); CountDownLatch start = new CountDownLatch(1); CountDownLatch done = new CountDownLatch(10); for (int i = 0; i < 10; i++) { new Thread(l. new Worker(start, done)).start(); } try { //do something start.countDown(); //do something done.await(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); }
  12. 12. }}1.6 Executor FrameworkExecutor framework has API to create thread pool and submit tasks to beexecuted by thread pools.Executor interface has only one method execute that takes a runnable object.Executor thread pools can be created by calling factory methodsExecutors.newCachedThreadPool(): If thread is available, ot will be used elsenew thread will be created. Threads not used for 60 seconds will be removedfro cache.Executors.newFixedThreadPool(n); N threads are created and added to the pool.The tasks are stored in unbounded queue and pool threads pick up tasks fromthe queue. If thread terminates due to failure, new thread is created andadded to pool.Executor.newSingleThredExecutor(): A pool of single thread.Executor Usage:package com.concur.executor;import;import;import;import java.util.concurrent.Executor;import java.util.concurrent.Executors;public class WebServer { Executor pool = Executors.newFixedThreadPool(50); public static void main(String[] args) throws IOException{ WebServer ws = new WebServer(); ServerSocket ssocket= new ServerSocket(80); while(true) { Socket soc = ssocket.accept(); Runnable r = new Runnable(){
  13. 13. @Override public void run() { handle(soc); } }; ws.pool.execute(r); } }}1.7 FutureFuture represents a task and serves as a wrapper for the tasks. The task maynot have started execution, or may be executing or may have completed. Theresult of the task can be obtained by calling future.get().future.get() returns immediately if task is completed, else it blocks tillthe task gets completed.FutureTask is an implementation of future interface. It also implementsrunnable interface and allows the task to be submitted toexecutor.execute(Runnable r).Bow code snippet shows usage of future task class to implement a thread safecache.package com.concur.cache;import java.util.concurrent.Callable;import java.util.concurrent.ConcurrentHashMap;import java.util.concurrent.ConcurrentMap;import java.util.concurrent.ExecutionException;import java.util.concurrent.Executor;import java.util.concurrent.Executors;import java.util.concurrent.FutureTask;public class SimpleCache <K, V> { private ConcurrentMap<K, FutureTask<V>> cache = new ConcurrentHashMap(); Executor pool = Executors.newFixedThreadPool(10); public V get(final K key) throws InterruptedException, ExecutionException { FutureTask<V> val = cache.get(key); if(val==null) { Callable<V> c = new Callable<V>(){ @Override
  14. 14. public V call() { System.out.println("Cache Miss"); return (V)new Integer(key.hashCode()); } }; val = new FutureTask<V>(c); FutureTask<V> oldVal=cache.putIfAbsent(key, val); if(oldVal==null){ //this thread should execute future task to get the actualcache value associated with key pool.execute(val); }else { //assign val to oldVal as other thread has won the race tostore its future task in concurrent map val=oldVal; } }else{ System.out.println("Cache Hit"); } return val.get(); } public static void main(String[] args) { try { SimpleCache<String,Integer > sc = newSimpleCache<String,Integer>(); System.out.println(sc.get("Hello")); System.out.println(sc.get("Hello")); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ExecutionException e) { // TODO Auto-generated catch block e.printStackTrace(); } }}