Java Concurrency Gotchas

15,217 views
15,434 views

Published on

Common Java concurrency problems and how to fix them.

Published in: Technology

Java Concurrency Gotchas

  1. 1. Java Concurrency Gotchas Alex Miller
  2. 2. Questions to answer • What are common concurrency problems? • Why are they problems? • How do I detect these problems? • How to I correct these problems?
  3. 3. Areas of Focus • Shared Data • Coordination • Performance
  4. 4. Areas of Focus { • Locking • Shared Data • Visibility • Atomicity • Coordination • Safe Publication • Performance
  5. 5. Unprotected Field Access A What happens if we modify data without locking?
  6. 6. Writer Readers Shared state
  7. 7. Locking A
  8. 8. Shared 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 { return FORMAT.parse(str); } ...and this mutates it outside synchronization public static void main(String arg[]) throws Exception { MutableStatics.parse(“Jan 1, 2000”); } }
  9. 9. Shared 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[]) throws Exception { MutableStatics.parse(“Jan 1, 2000”); } }
  10. 10. Shared mutable statics - ThreadLocal public class MutableStatics { private static final ThreadLocal<DateFormat> FORMAT = new ThreadLocal<DateFormat>() { @Override protected DateFormat initialValue() { return DateFormat.getDateInstance( DateFormat.MEDIUM); } }; public static Date parse(String str) throws ParseException { return FORMAT.get().parse(str); } }
  11. 11. Common JDK Examples Danger! Safe • DateFormat • Random • Calendar • Pattern • Matcher
  12. 12. Synchronization private int myField; synchronized( What goes here? ) { myField = 0; }
  13. 13. DO NOT: synchronize on null MyObject obj = null; synchronized( obj ) { NullPointerException! // work }
  14. 14. DO NOT: change instance MyObject obj = new MyObject(); synchronized( obj ) { obj = new MyObject(); no longer synchronizing on same object! }
  15. 15. DO NOT: synch on string literals private static final String LOCK = “LOCK”; synchronized( LOCK ) { // work What is the scope of LOCK? }
  16. 16. DO NOT: synch on autoboxed vals private static final Integer LOCK = 0; synchronized( LOCK ) { What is the scope of LOCK? // work }
  17. 17. DO NOT: synch on ReentrantLock Lock lock = new ReentrantLock(); synchronized(lock) { // ... Probably not what you meant here } Lock lock = new ReentrantLock(); lock.lock(); Probably more like this... try { // ... } finally { lock.unlock(); }
  18. 18. What should I lock on? // The field you’re protecting private final Map map = ... synchronized(map) { // ...access map } // Explicit lock object private final Object lock = new Object(); synchronized(lock) { // ...modify state }
  19. 19. Visibility
  20. 20. Visibility problems int x = 5; Thread 1: if(x == 5) { x = 10; } Thread 2: System.out.println(x);
  21. 21. Visibility problems volatile int x = 5; Thread 1: if(x == 5) { x = 10; } Thread 2: System.out.println(x);
  22. 22. Inconsistent Synchronization public class SomeData { private final Map data = new HashMap(); public void set(String key, String value) { synchronized(data) { Protecting writes data.put(key, value); } } public String get(String key) { return data.get(key); ...but not reads } }
  23. 23. Double-checked locking public 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; } }
  24. 24. Double-checked locking public 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; } }
  25. 25. 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; } }
  26. 26. 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; } }
  27. 27. volatile arrays public final class VolatileArray { private volatile boolean[] vals; public void flip(int i) { Is the value of vals[i] vals[i] = true; visible to other threads? } public boolean flipped(int i) { return vals[i]; } }
  28. 28. Atomicity
  29. 29. Volatile counter public class Counter { private volatile int count; public int next() { return count++; Looks atomic to me! } }
  30. 30. AtomicInteger counter public class Counter { private AtomicInteger count = new AtomicInteger(); public int next() { return count.getAndIncrement(); } Really atomic via } encapsulation over multiple actions
  31. 31. 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 table.get(key); READ return null; } else { // doesn't exist, create new value WRITE return table.put(key, value); } }
  32. 32. 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 table.get(key); READ return null; } else { // doesn't exist, create new value WRITE return table.put(key, value); } }
  33. 33. Participate in lock public Object putIfAbsent( Hashtable table, Object key, Object value) { Hashtable is thread-safe synchronized(table) { if(table.containsKey(key)) { table.get(key); return null; } else { return table.put(key, value); } } }
  34. 34. Encapsulated compound actions public Object putIfAbsent( ConcurrentHashMap table, Object key, Object value) { return table.putIfAbsent(key, value); } Encpasulation FTW!
  35. 35. 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? }
  36. 36. Assignment of 64 bit values - volatile public class LongAssignment { private volatile long x; public void setLong(long val) { x = val; } }
  37. 37. Safe publication
  38. 38. 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 } }
  39. 39. Starting thread in constructor public class Cache { private final Thread cleanerThread; public Cache() { cleanerThread = new Thread(new Cleaner(this)); cleanerThread.start(); this escapes again! } // Clean will call back to this method public void cleanup() { // clean up Cache } }
  40. 40. Static factory method public class Cache { // ... public static Cache newCache() { Cache cache = new Cache(); cache.startCleanerThread(); return cache; } }
  41. 41. Areas of Focus • Shared Data • Coordination • Performance { • Threads • Wait/notify
  42. 42. Threads
  43. 43. • THOU SHALT NOT: • call Thread.stop() All monitors unlocked by ThreadDeath • call Thread.suspend() or Thread.resume() Can lead to deadlock • call Thread.destroy() Not implemented (or safe) • call Thread.run() Wonʼt start Thread! Still in caller Thread. • use ThreadGroups Use a ThreadPoolExecutor instead.
  44. 44. 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(); lock.notifyAll(); }
  45. 45. Condition is similar private final Lock lock = new ReentrantLock(); private final Condition condition = lock.newCondition(); public void waitTillChange() { lock.lock(); try { while(! someCondition()) condition.await(); } finally { lock.unlock(); } Condition is more flexible than wait/notify. } public void change() { lock.lock(); try { satisfyCondition(); condition.signalAll(); } finally { lock.unlock(); } }
  46. 46. Areas of Focus • Shared Data • Coordination • Deadlock • Performance { • Spin wait • Thread contention
  47. 47. Deadlock // Thread 1 synchronized(lock1) { synchronized(lock2) { // stuff } } Classic deadlock. // Thread 2 synchronized(lock2) { synchronized(lock1) { // stuff } }
  48. 48. Deadlock avoidance • Lock splitting • Lock ordering • Lock timeout • tryLock
  49. 49. Spin wait // Not efficient private volatile boolean flag = false; public void waitTillChange() { while(! flag) { Thread.sleep(100); Spin on flag, waiting for a change. } } public void change() { flag = true; }
  50. 50. Replace with Condition private final Lock lock = new ReentrantLock(); private final Condition condition = lock.newCondition(); private boolean flag = false; public void waitTillChange() { lock.lock(); try { while(! flag) condition.await(); } finally { lock.unlock(); Better but longer. } } public void change() { lock.lock(); try { flag = true; condition.signalAll(); } finally { lock.unlock(); } }
  51. 51. CountDownLatch private final CountDownLatch latch = new CountDownLatch(1); public void waitTillChange() { latch.await(); } public void change() { latch.countDown(); Coordination classes } like CountDownLatch and CyclicBarrier cover many common uses better than Condition.
  52. 52. Lock contention x Hash f(x) Bucket 0 Bucket 1 Bucket 2 Bucket 3
  53. 53. Lock striping x Hash g(x) Hash f(x) Hash f(x) Bucket 0 Bucket 1 Bucket 0 Bucket 1
  54. 54. 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 } }
  55. 55. Final Exam public class StatisticsImpl implements Statistics, StatisticsImplementor { private long queryExecutionCount; Single shared lock for ALL stat values public synchronized void queryExecuted( String hql, int rows, long time) { queryExecutionCount++; // ... other stat collection } public long getQueryExecutionCount() { return queryExecutionCount; Read shared value Non-atomic read } w/o synchronization of long value public synchronized void clear() { queryExecutionCount = 0; Race condition if reading // ... clear all other stats stat and clearing } }
  56. 56. Thanks... Twitter http://twitter.com/puredanger Blog http://tech.puredanger.com Concurrency http://concurrency.tumblr.com links http://refcardz.dzone.com/refcardz/ Refcard core-java-concurrency Slides http://slideshare.net/alexmiller

×