Java Concurrency Gotchas
Upcoming SlideShare
Loading in...5
×

Like this? Share it with your network

Share

Java Concurrency Gotchas

  • 13,768 views
Uploaded on

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

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

More in: Technology , Business
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
13,768
On Slideshare
13,241
From Embeds
527
Number of Embeds
19

Actions

Shares
Downloads
555
Comments
0
Likes
41

Embeds 527

http://tech.puredanger.com 354
http://www.net4java.com 54
http://www.slideshare.net 42
http://www.jornaljava.com 25
http://puredanger.com 18
http://d10036.blogspot.com 7
http://www.linkedin.com 4
http://feeds2.feedburner.com 4
http://fujohnwang.appspot.com 4
http://itspice.net 4
http://processingbigdata.com:9000 2
http://blog.net4java.in 2
http://127.0.0.1 1
http://d10036.blogspot.com.es 1
http://feeds.feedburner.com 1
http://postupost.com 1
http://s.deeeki.com 1
http://webcache.googleusercontent.com 1
https://www.linkedin.com 1

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. Java™ Platform Concurrency Gotchas Alex Miller Terracotta
  • 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. Taxonomy of Concurrency Gotchas > Shared Data > Coordination > Performance 3
  • 4. Shared Data > Locking > Visibility > Atomicity > Safe Publication 4
  • 5. What happens if we modify data without locking? 5
  • 6. What happens if we modify data without locking? Hint: itʼs not good. 5
  • 7. 6
  • 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. 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. Synchronization private int myField; synchronized( What goes here? ) { myField = 0; } 9
  • 11. DO NOT synchronize on null MyObject obj = null; synchronized( obj ) { NullPointerException! // stuff } 10
  • 12. DO NOT change instance MyObject obj = new MyObject(); synchronized( obj ) { No longer synchronizing obj = new MyObject(); on the same object! } 11
  • 13. 12
  • 14. DO NOT synchronize on string literals private static final String LOCK = “LOCK”; synchronized( LOCK ) { What is the scope // work of LOCK? } 13
  • 15. DO NOT synchronize on autoboxed instances private static final Integer LOCK = 0; synchronized( LOCK ) { What is the scope // work of LOCK? } 14
  • 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. 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. Visibility 17
  • 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. 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. 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. 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. 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. 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. 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. Atomicity 24
  • 27. Volatile counter public class Counter { private volatile int count; public int next() { return count++; Looks atomic to me! } } 25
  • 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. 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. 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. 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. Encapsulated compound actions public Object putIfAbsent( ConcurrentHashMap table, Object key, Object value) { table.putIfAbsent(key, value); } 29
  • 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. Assignment of 64 bit values - volatile public class LongAssignment { private volatile long x; public void setLong(long val) { x = val; } } 31
  • 35. Safe publication Intentionally left blank. 32
  • 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. 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. Static factory method public class Cache { // ... public static Cache newCache() { Cache cache = new Cache(); cache.startCleanerThread(); return cache; } } 35
  • 39. Coordination > Threads > wait/notify 36
  • 40. Threads > DO NOT: • Call Thread.stop() • Call Thread.suspend() or Thread.resume() • Call Thread.destroy() • Call Thread.run() • Use ThreadGroups 37
  • 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. Performance > Deadlock > Spin wait > Lock contention 39
  • 43. Deadlock // Thread 1 synchronized(lock1) { synchronized(lock2) { Classic deadlock. // stuff } } // Thread 2 synchronized(lock2) { synchronized(lock1) { // stuff } } 40
  • 44. Deadlock avoidance > Lock splitting > Lock ordering > Lock timeout > tryLock 41
  • 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. 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. Lock contention x Hash f(x) Bucket 0 Bucket 1 Bucket 2 Bucket 3 44
  • 48. Lock striping x Hash g(x) Hash f(x) Hash f(x) Bucket 0 Bucket 1 Bucket 0 Bucket 1 45
  • 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. 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. 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. 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. 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. Alex Miller Terracotta Blog: http://tech.puredanger.com Twitter: @puredanger