Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Java Concurrency Gotchas

11,397 views

Published on

An overview of Java Concurrency bug patterns. JavaOne 2009 presentation.

Published in: Technology, Business

Java Concurrency Gotchas

  1. 1. Java™ Platform Concurrency Gotchas Alex Miller Terracotta
  2. 2. Questions to answer > What are common concurrency problems? > Why are they problems? > How do I detect these problems? > How do I correct these problems? 2
  3. 3. Taxonomy of Concurrency Gotchas > Shared Data > Coordination > Performance 3
  4. 4. Shared Data > Locking > Visibility > Atomicity > Safe Publication 4
  5. 5. What happens if we modify data without locking? 5
  6. 6. What happens if we modify data without locking? Hint: itʼs not good. 5
  7. 7. 6
  8. 8. Mutable Statics public class MutableStatics { FORMAT is mutable private static final DateFormat FORMAT = DateFormat.getDateInstance(DateFormat.MEDIUM); public static Date parse(String str) throws ParseException { ...and this mutates it return FORMAT.parse(str); outside synchronization } public static void main(String arg[]) { MutableStatics.parse(“Jan 1, 2000”); } } 7
  9. 9. Mutable Statics - instance per call public class MutableStatics { public static Date parse(String str) throws ParseException { DateFormat format = DateFormat.getDateInstance(DateFormat.MEDIUM); return format.parse(str); } public static void main(String arg[]) { MutableStatics.parse(“Jan 1, 2000”); } } 8
  10. 10. Synchronization private int myField; synchronized( What goes here? ) { myField = 0; } 9
  11. 11. DO NOT synchronize on null MyObject obj = null; synchronized( obj ) { NullPointerException! // stuff } 10
  12. 12. DO NOT change instance MyObject obj = new MyObject(); synchronized( obj ) { No longer synchronizing obj = new MyObject(); on the same object! } 11
  13. 13. 12
  14. 14. DO NOT synchronize on string literals private static final String LOCK = “LOCK”; synchronized( LOCK ) { What is the scope // work of LOCK? } 13
  15. 15. DO NOT synchronize on autoboxed instances private static final Integer LOCK = 0; synchronized( LOCK ) { What is the scope // work of LOCK? } 14
  16. 16. DO NOT synchronize on ReentrantLock final Lock lock = new ReentrantLock(); synchronized( lock ) { Probably not what // work you meant here } final Lock lock = new ReentrantLock(); lock.lock(); Probably should try { be this instead // ... } finally { lock.unlock(); } 15
  17. 17. What should I lock on? // The field you are protecting private final Map map = ... synchronized(map) { // ...access map } // Or an explicit lock object private final Object lock = new Object(); synchronized(lock) { // ... modify state } 16
  18. 18. Visibility 17
  19. 19. Inconsistent Synchronization public class SomeData { private final Map data = new HashMap(); public void put(String key, String value) { synchronized(data) { data.put(key, value); Modified under synchronization } } public String get(String key) { return data.get(key); Read without synchronization } } 18
  20. 20. Double-checked locking public final class Singleton { private static Singleton instance; public static Singleton getInstance() { if(instance == null) { Attempt to avoid synchronization synchronized(Singleton.class) { if(instance == null) { instance = new Singleton(); } } } return instance; } } 19
  21. 21. Double-checked locking public final class Singleton { private static Singleton instance; public static Singleton getInstance() { if(instance == null) { READ synchronized(Singleton.class) { if(instance == null) { READ instance = new Singleton(); WRITE } } } return instance; } } 19
  22. 22. Double-checked locking - volatile public class Singleton { private static volatile Singleton instance; public static Singleton getInstance() { if(instance == null) { synchronized(Singleton.class) { if(instance == null) { instance = new Singleton(); } } } return INSTANCE; } 20
  23. 23. Double-checked locking - initialize on demand public class Singleton { private static class SingletonHolder { private static final Singleton instance = new Singleton(); } public static Singleton getInstance() { return SingletonHolder.instance; } } 21
  24. 24. Racy single-check public final class String { private int hash; // default to 0 private final char[] value; // immutable public int hashCode() { int h = hash; if(h == 0) { // ... compute value for h from data hash = h; } return h; } } 22
  25. 25. volatile arrays public final class VolatileArray { private volatile boolean[] vals; public void flip(int i) { vals[i] = true; Is the value of vals[i] } visible to other threads? public boolean flipped(int i) { return vals[i]; } } 23
  26. 26. Atomicity 24
  27. 27. Volatile counter public class Counter { private volatile int count; public int next() { return count++; Looks atomic to me! } } 25
  28. 28. AtomicInteger counter public class Counter { private final AtomicInteger count = new AtomicInteger(); public int next() { Really atomic by return count.getAndIncrement(); encapsulating } multiple actions } 26
  29. 29. Composing atomic actions public Object putIfAbsent( Hashtable table, Object key, Object value) { Hashtable is thread-safe if(table.containsKey(key)) { READ // already present, return existing value return table.get(key); READ } else { // doesn’t exist, create and return new value table.put(key, value); WRITE return value; } } 27
  30. 30. Composing atomic actions public Object putIfAbsent( Hashtable table, Object key, Object value) { Hashtable is thread-safe if(table.containsKey(key)) { READ // already present, return existing value return table.get(key); READ } else { // doesn’t exist, create and return new value table.put(key, value); WRITE return value; } } 27
  31. 31. Participate in lock public Object putIfAbsent( Hashtable table, Object key, Object value) { synchronized(table) { Protect with synchronization if(table.containsKey(key)) { return table.get(key); } else { table.put(key, value); return value; } } } 28
  32. 32. Encapsulated compound actions public Object putIfAbsent( ConcurrentHashMap table, Object key, Object value) { table.putIfAbsent(key, value); } 29
  33. 33. Assignment of 64 bit values public class LongAssignment { private long x; public void setLong(long val) { x = val; Looks atomic to me - } but is it? } 30
  34. 34. Assignment of 64 bit values - volatile public class LongAssignment { private volatile long x; public void setLong(long val) { x = val; } } 31
  35. 35. Safe publication Intentionally left blank. 32
  36. 36. Listener in constructor public interface DispatchListener { void newFare(Customer customer); } public class Taxi implements DispatchListener { public Taxi(Dispatcher dispatcher) { dispatcher.registerListener(this); We just published a // other initialization reference to this - oops! } public void newFare(Customer customer) { // go to new customer's location } } 33
  37. 37. Starting thread in constructor public class Cache { private final Thread cleanerThread; public Cache() { this escapes again! cleanerThread = new Thread(new Cleaner(this)); cleanerThread.start(); } // Cleaner calls back to this method public void cleanup() { // clean up Cache } } 34
  38. 38. Static factory method public class Cache { // ... public static Cache newCache() { Cache cache = new Cache(); cache.startCleanerThread(); return cache; } } 35
  39. 39. Coordination > Threads > wait/notify 36
  40. 40. Threads > DO NOT: • Call Thread.stop() • Call Thread.suspend() or Thread.resume() • Call Thread.destroy() • Call Thread.run() • Use ThreadGroups 37
  41. 41. wait/notify // Thread 1 synchronized(lock) { You must synchronize. while(! someCondition()) { Always wait in a loop. lock.wait(); } } // Thread 2 synchronized(lock) { Synchronize here too. satisfyCondition(); Update condition! lock.notifyAll(); } 38
  42. 42. Performance > Deadlock > Spin wait > Lock contention 39
  43. 43. Deadlock // Thread 1 synchronized(lock1) { synchronized(lock2) { Classic deadlock. // stuff } } // Thread 2 synchronized(lock2) { synchronized(lock1) { // stuff } } 40
  44. 44. Deadlock avoidance > Lock splitting > Lock ordering > Lock timeout > tryLock 41
  45. 45. Spin wait // Not efficient private volatile boolean flag = false; public void waitTillChange() { while(! flag) { Spin on flag, Thread.sleep(100); waiting for change } } public void change() { flag = true; } 42
  46. 46. Replace with wait/notify private final Object lock = new Object(); private boolean flag = false; public void waitTillChange() { synchronized(lock) { while(! flag) Wait/notify is far more lock.wait(); efficient than spin wait. } } public void change() { synchronized(lock) { flag = true; lock.notifyAll(); } } 43
  47. 47. Lock contention x Hash f(x) Bucket 0 Bucket 1 Bucket 2 Bucket 3 44
  48. 48. Lock striping x Hash g(x) Hash f(x) Hash f(x) Bucket 0 Bucket 1 Bucket 0 Bucket 1 45
  49. 49. Final Exam public class StatisticsImpl implements Statistics, StatisticsImplementor { private long queryExecutionCount; public synchronized void queryExecuted(String hql, int rows, long time) { queryExecutionCount++; // ... other stat collection } public long getQueryExecutionCount() { return queryExecutionCount; } public synchronized void clear() { queryExecutionCount = 0; // ... clear all other stats } } 46
  50. 50. Final Exam public class StatisticsImpl implements Statistics, StatisticsImplementor { private long queryExecutionCount; public synchronized void queryExecuted(String hql, int rows, long time) { queryExecutionCount++; // ... other stat collection } public long getQueryExecutionCount() { return queryExecutionCount; Read of shared value } without synchronization public synchronized void clear() { queryExecutionCount = 0; // ... clear all other stats } } 46
  51. 51. Final Exam public class StatisticsImpl implements Statistics, StatisticsImplementor { private long queryExecutionCount; public synchronized void queryExecuted(String hql, int rows, long time) { queryExecutionCount++; // ... other stat collection } public long getQueryExecutionCount() { return queryExecutionCount; Read of shared value Non-atomic read } without synchronization of long value public synchronized void clear() { queryExecutionCount = 0; // ... clear all other stats } } 46
  52. 52. Final Exam public class StatisticsImpl implements Statistics, StatisticsImplementor { private long queryExecutionCount; public synchronized void queryExecuted(String hql, int rows, long time) { queryExecutionCount++; // ... other stat collection } public long getQueryExecutionCount() { return queryExecutionCount; Read of shared value Non-atomic read } without synchronization of long value public synchronized void clear() { queryExecutionCount = 0; Race condition if reading stat and // ... clear all other stats clearing - could be compound action } } 46
  53. 53. Final Exam public class StatisticsImpl implements Statistics, StatisticsImplementor { private long queryExecutionCount; public synchronized void queryExecuted(String hql, int rows, long time) { Single shared lock for ALL stat values queryExecutionCount++; // ... other stat collection } public long getQueryExecutionCount() { return queryExecutionCount; Read of shared value Non-atomic read } without synchronization of long value public synchronized void clear() { queryExecutionCount = 0; Race condition if reading stat and // ... clear all other stats clearing - could be compound action } } 46
  54. 54. Alex Miller Terracotta Blog: http://tech.puredanger.com Twitter: @puredanger

×