JavaConcurrencyRahul RevoGarrett ShortCreative MindaysMarch 3, 2011
Why care about ConcurrencyThe free lunch is over – Herb SutterMulti-core processors are hereApplications need to be concurrent to exploit CPUsMarch 2005 http://www.gotw.ca/publications/concurrency-ddj.htm23 March 2011
Language support for concurrencyThreads – C, C++, Java …Actors – ErlangSingle thread and WebWorker - JavascriptJavaThreading support in core libraryMonitor based synchronizationand new concurrency APIs33 March 2011
Thread safe code 1Manage access to shared mutable dataWhyOrder of executionStale data visibleAtomicity43 March 2011x= y = 0Thread 1Thread 2y = 1 j = xx = 1i = yCan i = 0 and j = 0 ?
Thread safe code        2Manage access to shared mutable dataHowDon’t share state variables across threadsUse java.lang.ThreadLocalMake objects immutableGuard access53 March 2011
volatileReads and writes provide correct visibilityHappens-before relationship, like a synchronized blockclass Worker implements Runnable { private volatileboolean cancelled = false; run() {   while(!cancelled) { //do work } } cancel() { cancelled = true; }}63 March 2011
Immutable ObjectsAn object is immutable ifState cannot be modified after constructionAll fields are finalthis reference does not escape during construction73 March 2011class Cart {  private List<String> items;  public Cart(List items) { this.items = items; }  public List getItems() { return items; }}class ImmutableCart {  private final List<String> items;  public ImmutableCart(List<String> thoseItems) {    items = Collections.unmodifiableList(new ArrayList<>(thoseItems));  }  public List getItems() { return items; }}
Annotations@GuardedBy(“some-lock-or-var”)The field or method can only be accessed when holding a particular lock@ImmutableIts state cannot be seen to change by callers@NotThreadSafeClarifying the non-thread-safety of a class that might otherwise be assumed to be thread-safe@ThreadSafeThis means that no sequences of accesses may put the object into an invalid state, regardless of the interleaving of those actions without requiring any additional synchronization on the part of the caller.83 March 2011
java.util.concurrentCollectionsBlockingQueueConcurrentMapCopyOnWriteTask ExecutionExecutor, ExecutorServiceExecutorsCallableFuture93 March 2011
Consumer:synchronized (obj) {    while (! workToDo) {obj.wait();    }    // get next item from the queueworkToDo = false; }// do work on the itemProducer:synchronized (obj) {    if (! workToDo) {        // add work to queueworkToDo = true;    }obj.notifyAll();}103 March 2011Producer-Consumer Pattern                1
class Producer implements Runnable {   private final BlockingQueue queue;   Producer(BlockingQueue q) { queue = q; }   public void run() {     while (true) {queue.put(produce());      }   }   private Object produce() { ... } }113 March 2011Producer-Consumer Pattern                3
class Consumer implements Runnable {   private final BlockingQueue queue;   Consumer(BlockingQueue q) { queue = q; }   public void run() {     while (true) {consume(queue.take());     }   }   private void consume(Object x) { ... } }123 March 2011Producer-Consumer Pattern                4
133 March 2011void main() {BlockingQueue q = new SomeQueueImplementation();  Producer p = new Producer(q);  Consumer c1 = new Consumer(q);  Consumer c2 = new Consumer(q);  new Thread(p).start();  new Thread(c1).start();  new Thread(c2).start();}Producer-Consumer Pattern                2
Concurrent HashMap1Completely plug-compatible, implements the same Map interface we all know and love today.All operations are thread-safe, however retrieval operations do not involve locking the entire table. No ConcurrentModificationException!  Creation of the an iterator or some enumeration reflect the state at that time. Note: iterators are intended to be used by only one thread at a time. Resizing is relatively slow operation, so try to provide good estimates of expected table sizes in constructors. A new optional 'concurrencyLevel' constructor argument (default 16),  which is used as a hint for internal sizing. The value is the estimated number of concurrent updating threads. A value of 1 is appropriate there is only one writer and all other threads are readers. 143 March 2011
Concurrent HashMap2Add an item in a hash map only if it doesn’t already existOld way:synchronize (obj)  {if (! map.containsKey(key)) {map.put(key, value);}}New Way:map.putIfAbsent(key, value);153 March 2011
Concurrent 3Additional methods beyond HashMapboolean remove(Object key, Object value)If this key is mapped to this value, remove it.V replace(K key, V value)If this key is mapped to any value, then map it instead to this new value. booleanreplace(K key, V oldValue, V newValue)If this key is mapped to this  old value, then map it to this new value.163 March 2011
CopyOnWriteArrayListCopyOnWriteArrayList makes a  fresh copy of the underlying array for mutative operations like addUse when lots of reads vs writes like event listenersList list1 = new CopyOnWriteArrayList(...);List list2 = new ArrayList(...);Iterator itor1 = list1.iterator();Iterator itor2 = list2.iterator();list1.add("New");list2.add("New");printAll(itor1);printAll(itor2); // throws ConcurrentModificationException173 March 2011
Collection ClassesDo not use the old synchronized classes:Vector, Stack, HashtableUse the non-synchronized version when that’s all you need:Arraylist, LinkedList, HashMapWhen Synchronization is needed, use the new java.util.concurrentclasses:ArrayBlockingQueue, LinkedBlockingQueue, ConcurrentHashMap183 March 2011
Task Execution     1Decouple the task submission from task execution by using an ExecutorDefine an execution policyIn what thread will execution happenWhat is the order of execution How many tasks run concurrently193 March 2011
Task Execution                                   2203 March 2011class ThreadPerTaskExecutor implements Executor {  public void execute(Runnable r) {    new Thread(r).start();  }}class DirectExecutor implements Executor {  public void execute(Runnable r) {r.run();   } }Executor executor = anExecutor; executor.execute(new Runnable1());executor.execute(new Runnable2());And...FixedThreadPoolCachedThreadPoolScheduledThreadPool
ExecutorServiceMethods provided for shutdownGet status of tasks via FutureExecutorService extends Executor {  void execute(Runnable r);  Future<T> submit(Callable<T> t …);  void shutdown();booleanisShutdown();booleanisTerminated();}213 March 2011
What’s the FutureCallable – Runnable on steroidsCallable<V> {  V call() throws Exception;}Future – result of an asynchronous computationFuture<V> {  V get();boolean cancel();booleanisCancelled();booleanisDone();}223 March 2011
Asynchronous resultsclass QuoteProcessor {ExecutorService exec = ...processQuotes() {    List<QuoteTask> tasks = ...    List<Future<TravelQuote>> futures = exec.invokeAll(tasks);    List<TravelQuote> quotes = new ArrayList<>();    for(Future<TravelQuote> f : futures)quotes.add(f.get());    return quotes;  }}233 March 2011
Scheduled ProcessingUse ScheduledThreadPoolExecutor instead of java.util.Timerexecutor = new ScheduledThreadPoolExecutor();executor.scheduleWithFixedDelay(new BatchProcess(), 10, 10, unit);// executor.scheduleWithFixedRate(...)// other threads are adding to this queue:queue = new LinkedBlockingDeque();class BatchProcess implements Runnable {  public void run() {    List data = new ArrayList ();queue.drainTo(data);    // process data list in a batch  }}243 March 2011
ThreadPoolExecutorThreadPoolExecutor configurationsCore and maximum pool On-demand constructionCreating new threadsKeep-alive timesQueueRejected tasksHook methodsQueue maintenanceFinalization253 March 2011
More concurrent APIsLocksConditionLockReadWriteLockReentrantLockAtomicBoolean, longMiscellaneousSemaphore, Barriers263 March 2011
Fork Join (coming in JDK 7)The fork/join framework is designed to make divide-and-conquer algorithms easy to parallelize.For example, recursive algorithms where the control path branches out over a few paths and they each process a part of the data set.  The typical setup is a new class is created that extends either the RecursiveActionor RecursiveTaskclass.   273 March 2011
Fork Join Exampleclass ForkJoinMain {    main() {ForkJoinPool pool = new ForkJoinPool();int[] data = [data set];        long sum = pool.invoke(            new SumTask(data, 0, data.length - 1));        long sumSquares = pool.invoke(             new SumSquaresTask(data, 0, data.length - 1));        long max = pool.invoke(            new MaxTask(data, 0, data.length - 1));    }}283 March 2011
Fork Join Exampleabstract class AbstractExampleTask extends RecursiveTask<Long> {AbstractExampleTask(int[] data, intfirstIndex, intlastIndex) { ... }    Long compute() {        if (getLength() < THRESHOLD) {            return directlySolve();        }AbstractExampleTask left = getSubtask(first, half()-1);AbstractExampleTask right = getSubtask(half(), last);left.fork();right.fork();        return handleResults(left.join(), right.join());    }}293 March 2011
class SumSquaresTask extends AbstractExampleTask {SumSquaresTask(...) { ... }    Long directlySolve() {        long accum = 0L;        for (int i = 0; i < getLength(); i++) {accum = accum + (get(i) * get(i));        }        return accum;    }    Long handleResults(Long leftLong, Long rightLong) {        return leftLong + rightLong;    }AbstractExampleTaskgetSubtask(int start, int end) {        return new SumSquaresTask(data, start, end);    }}303 March 2011Fork Join Example
SummaryRecommended order of useUse high level java.util.concurrent APIsLow level locking with synchronizationVolatile variables and java.util.concurrent.atomic classesPrefer correctness over speed313 March 2011
Q&A323 March 2011

Concurrent talk

  • 1.
  • 2.
    Why care aboutConcurrencyThe free lunch is over – Herb SutterMulti-core processors are hereApplications need to be concurrent to exploit CPUsMarch 2005 http://www.gotw.ca/publications/concurrency-ddj.htm23 March 2011
  • 3.
    Language support forconcurrencyThreads – C, C++, Java …Actors – ErlangSingle thread and WebWorker - JavascriptJavaThreading support in core libraryMonitor based synchronizationand new concurrency APIs33 March 2011
  • 4.
    Thread safe code1Manage access to shared mutable dataWhyOrder of executionStale data visibleAtomicity43 March 2011x= y = 0Thread 1Thread 2y = 1 j = xx = 1i = yCan i = 0 and j = 0 ?
  • 5.
    Thread safe code 2Manage access to shared mutable dataHowDon’t share state variables across threadsUse java.lang.ThreadLocalMake objects immutableGuard access53 March 2011
  • 6.
    volatileReads and writesprovide correct visibilityHappens-before relationship, like a synchronized blockclass Worker implements Runnable { private volatileboolean cancelled = false; run() { while(!cancelled) { //do work } } cancel() { cancelled = true; }}63 March 2011
  • 7.
    Immutable ObjectsAn objectis immutable ifState cannot be modified after constructionAll fields are finalthis reference does not escape during construction73 March 2011class Cart { private List<String> items; public Cart(List items) { this.items = items; } public List getItems() { return items; }}class ImmutableCart { private final List<String> items; public ImmutableCart(List<String> thoseItems) { items = Collections.unmodifiableList(new ArrayList<>(thoseItems)); } public List getItems() { return items; }}
  • 8.
    Annotations@GuardedBy(“some-lock-or-var”)The field ormethod can only be accessed when holding a particular lock@ImmutableIts state cannot be seen to change by callers@NotThreadSafeClarifying the non-thread-safety of a class that might otherwise be assumed to be thread-safe@ThreadSafeThis means that no sequences of accesses may put the object into an invalid state, regardless of the interleaving of those actions without requiring any additional synchronization on the part of the caller.83 March 2011
  • 9.
  • 10.
    Consumer:synchronized (obj) { while (! workToDo) {obj.wait(); } // get next item from the queueworkToDo = false; }// do work on the itemProducer:synchronized (obj) { if (! workToDo) { // add work to queueworkToDo = true; }obj.notifyAll();}103 March 2011Producer-Consumer Pattern 1
  • 11.
    class Producer implementsRunnable { private final BlockingQueue queue; Producer(BlockingQueue q) { queue = q; } public void run() { while (true) {queue.put(produce()); } } private Object produce() { ... } }113 March 2011Producer-Consumer Pattern 3
  • 12.
    class Consumer implementsRunnable { private final BlockingQueue queue; Consumer(BlockingQueue q) { queue = q; } public void run() { while (true) {consume(queue.take()); } } private void consume(Object x) { ... } }123 March 2011Producer-Consumer Pattern 4
  • 13.
    133 March 2011voidmain() {BlockingQueue q = new SomeQueueImplementation(); Producer p = new Producer(q); Consumer c1 = new Consumer(q); Consumer c2 = new Consumer(q); new Thread(p).start(); new Thread(c1).start(); new Thread(c2).start();}Producer-Consumer Pattern 2
  • 14.
    Concurrent HashMap1Completely plug-compatible,implements the same Map interface we all know and love today.All operations are thread-safe, however retrieval operations do not involve locking the entire table. No ConcurrentModificationException! Creation of the an iterator or some enumeration reflect the state at that time. Note: iterators are intended to be used by only one thread at a time. Resizing is relatively slow operation, so try to provide good estimates of expected table sizes in constructors. A new optional 'concurrencyLevel' constructor argument (default 16), which is used as a hint for internal sizing. The value is the estimated number of concurrent updating threads. A value of 1 is appropriate there is only one writer and all other threads are readers. 143 March 2011
  • 15.
    Concurrent HashMap2Add anitem in a hash map only if it doesn’t already existOld way:synchronize (obj) {if (! map.containsKey(key)) {map.put(key, value);}}New Way:map.putIfAbsent(key, value);153 March 2011
  • 16.
    Concurrent 3Additional methodsbeyond HashMapboolean remove(Object key, Object value)If this key is mapped to this value, remove it.V replace(K key, V value)If this key is mapped to any value, then map it instead to this new value. booleanreplace(K key, V oldValue, V newValue)If this key is mapped to this old value, then map it to this new value.163 March 2011
  • 17.
    CopyOnWriteArrayListCopyOnWriteArrayList makes a fresh copy of the underlying array for mutative operations like addUse when lots of reads vs writes like event listenersList list1 = new CopyOnWriteArrayList(...);List list2 = new ArrayList(...);Iterator itor1 = list1.iterator();Iterator itor2 = list2.iterator();list1.add("New");list2.add("New");printAll(itor1);printAll(itor2); // throws ConcurrentModificationException173 March 2011
  • 18.
    Collection ClassesDo notuse the old synchronized classes:Vector, Stack, HashtableUse the non-synchronized version when that’s all you need:Arraylist, LinkedList, HashMapWhen Synchronization is needed, use the new java.util.concurrentclasses:ArrayBlockingQueue, LinkedBlockingQueue, ConcurrentHashMap183 March 2011
  • 19.
    Task Execution 1Decouple the task submission from task execution by using an ExecutorDefine an execution policyIn what thread will execution happenWhat is the order of execution How many tasks run concurrently193 March 2011
  • 20.
    Task Execution 2203 March 2011class ThreadPerTaskExecutor implements Executor { public void execute(Runnable r) { new Thread(r).start(); }}class DirectExecutor implements Executor { public void execute(Runnable r) {r.run(); } }Executor executor = anExecutor; executor.execute(new Runnable1());executor.execute(new Runnable2());And...FixedThreadPoolCachedThreadPoolScheduledThreadPool
  • 21.
    ExecutorServiceMethods provided forshutdownGet status of tasks via FutureExecutorService extends Executor { void execute(Runnable r); Future<T> submit(Callable<T> t …); void shutdown();booleanisShutdown();booleanisTerminated();}213 March 2011
  • 22.
    What’s the FutureCallable– Runnable on steroidsCallable<V> { V call() throws Exception;}Future – result of an asynchronous computationFuture<V> { V get();boolean cancel();booleanisCancelled();booleanisDone();}223 March 2011
  • 23.
    Asynchronous resultsclass QuoteProcessor{ExecutorService exec = ...processQuotes() { List<QuoteTask> tasks = ... List<Future<TravelQuote>> futures = exec.invokeAll(tasks); List<TravelQuote> quotes = new ArrayList<>(); for(Future<TravelQuote> f : futures)quotes.add(f.get()); return quotes; }}233 March 2011
  • 24.
    Scheduled ProcessingUse ScheduledThreadPoolExecutorinstead of java.util.Timerexecutor = new ScheduledThreadPoolExecutor();executor.scheduleWithFixedDelay(new BatchProcess(), 10, 10, unit);// executor.scheduleWithFixedRate(...)// other threads are adding to this queue:queue = new LinkedBlockingDeque();class BatchProcess implements Runnable { public void run() { List data = new ArrayList ();queue.drainTo(data); // process data list in a batch }}243 March 2011
  • 25.
    ThreadPoolExecutorThreadPoolExecutor configurationsCore andmaximum pool On-demand constructionCreating new threadsKeep-alive timesQueueRejected tasksHook methodsQueue maintenanceFinalization253 March 2011
  • 26.
  • 27.
    Fork Join (comingin JDK 7)The fork/join framework is designed to make divide-and-conquer algorithms easy to parallelize.For example, recursive algorithms where the control path branches out over a few paths and they each process a part of the data set.  The typical setup is a new class is created that extends either the RecursiveActionor RecursiveTaskclass.   273 March 2011
  • 28.
    Fork Join ExampleclassForkJoinMain { main() {ForkJoinPool pool = new ForkJoinPool();int[] data = [data set]; long sum = pool.invoke( new SumTask(data, 0, data.length - 1)); long sumSquares = pool.invoke( new SumSquaresTask(data, 0, data.length - 1)); long max = pool.invoke( new MaxTask(data, 0, data.length - 1)); }}283 March 2011
  • 29.
    Fork Join Exampleabstractclass AbstractExampleTask extends RecursiveTask<Long> {AbstractExampleTask(int[] data, intfirstIndex, intlastIndex) { ... } Long compute() { if (getLength() < THRESHOLD) { return directlySolve(); }AbstractExampleTask left = getSubtask(first, half()-1);AbstractExampleTask right = getSubtask(half(), last);left.fork();right.fork(); return handleResults(left.join(), right.join()); }}293 March 2011
  • 30.
    class SumSquaresTask extendsAbstractExampleTask {SumSquaresTask(...) { ... } Long directlySolve() { long accum = 0L; for (int i = 0; i < getLength(); i++) {accum = accum + (get(i) * get(i)); } return accum; } Long handleResults(Long leftLong, Long rightLong) { return leftLong + rightLong; }AbstractExampleTaskgetSubtask(int start, int end) { return new SumSquaresTask(data, start, end); }}303 March 2011Fork Join Example
  • 31.
    SummaryRecommended order ofuseUse high level java.util.concurrent APIsLow level locking with synchronizationVolatile variables and java.util.concurrent.atomic classesPrefer correctness over speed313 March 2011
  • 32.

Editor's Notes

  • #2 http://www.gotw.ca/publications/concurrency-ddj.htmApplications will increasingly need to be concurrent if they want to fully exploit continuing exponential CPU throughput gainsEfficiency and performance optimization will get more, not less, importanthttp://www.infoq.com/interviews/pike-concurrency Concurrency is a programming model that lets you express things that are independent, as independent executions and parallelism is about running two things at the same time. They are not the same idea and a lot of people confuse them, they think they are the same thing, but concurrency and parallelism are best thought of as a different idea. You can write beautiful concurrent programs with no parallelism whatsoever and you can also write extremely parallel programs that are not remotely concurrent. Concurrency is a model, parallelism is a result - maybe that&apos;s the best way to say it.
  • #3 Different programming languages support concurrent programming in different waysThreading libraries, actor framework, no explicit threads!Thread is a unit of executionMultiple threads can be running at the same timeMonitors allow a single thread to be running inside a critical areaCommunicate by wait/notify mechanismAnd we’ll talk mainly about the new APIs
  • #4 Happens-beforeVisibilityCompilers can reorder codePage 341
  • #5 Page 54
  • #6 If cancelled is not volatile then the cancel set by another thread may never be visible
  • #7 Transfer of ownership of the thoseItemsCan have a copy strategy if returning something modifiable
  • #8 Writing multiple-threaded code is hardDebugging m-t code is hardedDebugging someone elses m-t code is even harder.So little documentation can go a long way to help.Help both you and the reader.Documentationof your sync policyCan download a jar that defines these annotations
  • #9 Avoid creating your own locks and synchronization blocks.Get out of the business of creating your own threads and managing their life cycle.
  • #10 Here’s the old way of doing a producer/consumer pattern.This creates a synchronized block around the management of a work queue and a Boolean control flag ----- Ugly and MessyMight have the ‘while’ as just and ‘if’Might be some code outside the synchronized blocks that messing with the queue, hard to be absolutely sure.
  • #11 Blocking Queue -- Waits for the queue to be non-empty when retrieving objects and waits for storage available when adding objects.Maybe capacity bounded.
  • #12 See how much cleaner and simpler this code is.
  • #13 There are 4-5 new classes that implement the BlockingQueue interface. Pick one that best suits your needs.So no messy obj synchronization, no calls to wait/notify.
  • #14 HashMaps are very common in code dealing with a large number of objects.If you are writing m-t code, consider a concurrent hash map.
  • #15 The concurrent hash map goes beyond the simple HashMap class with some useful methods, here’s one example.Even if the map was synchronize, you still need to create a synchronize block around compound calls.
  • #16 More compound operations combined in a single method.
  • #17 list should be used when the number of reads vastly outnumber the number of writes. This is because you are trading unnecessary synchronization for expensive array copying on each write.Here’s the problem: You have a collection with lots of readers and a few writers operating in a multi-thread environment. Two solutions: writers wait until it can gain exclusive access, or readers make a copy of the collection first.Here’s an example.Caveat: the readers may be operating on stale data. Mostly likely it doesn’t matter.
  • #18 Didn’t make JDK 6, based on JSR 166.Library (jsr166.jar) is available today.
  • #19 Here’s the problem:You have a huge data set that need to be processed. You want to gain performance by having multiple threads doing the computation.
  • #21 http://video.google.com/videoplay?docid=8394326369005388010http://www.javaconcurrencyinpractice.com/Effective java