Your SlideShare is downloading. ×
0
Java Concurrency
        Gotchas


           Alex Miller
Questions to answer

• What are common concurrency problems?
• Why are they problems?
• How do I detect these problems?
• ...
Areas of Focus

• Shared Data
• Coordination
• Performance
Areas of Focus


                 {
                     • Locking
• Shared Data        • Visibility
                     ...
Unprotected Field
     Access

              A



What happens if we modify data
      without locking?
Writer




Readers
          Shared state
Locking


  A
Shared Mutable Statics
public class MutableStatics {
                                           FORMAT is mutable
    priv...
Shared mutable statics -
        instance per call
public class MutableStatics {

    public static Date parse(String str)...
Shared mutable statics -
          ThreadLocal
public class MutableStatics {
  private static final ThreadLocal<DateFormat...
Common JDK Examples

Danger!        Safe

• DateFormat   • Random
• Calendar     • Pattern
• Matcher
Synchronization

private int myField;

synchronized( What goes here? ) {
  myField = 0;
}
DO NOT:
      synchronize on null
MyObject obj = null;

synchronized( obj ) { NullPointerException!
  // work
}
DO NOT:
                change instance
MyObject obj = new MyObject();

synchronized( obj ) {
  obj = new MyObject();
    ...
DO NOT:
  synch on string literals
private static final String LOCK = “LOCK”;
synchronized( LOCK ) {
  // work
           ...
DO NOT:
synch on autoboxed vals
private static final Integer LOCK = 0;
synchronized( LOCK ) { What is the scope of LOCK?
 ...
DO NOT:
synch on ReentrantLock
Lock lock = new ReentrantLock();
synchronized(lock) {
  // ...    Probably not what you mea...
What should I lock on?
// The field you’re protecting
private final Map map = ...
synchronized(map) {
  // ...access map
}...
Visibility
Visibility problems
int x = 5;

Thread 1:
if(x == 5) {
   x = 10;
}

               Thread 2:
               System.out.pr...
Visibility problems
volatile int x = 5;

Thread 1:
if(x == 5) {
   x = 10;
}

                      Thread 2:
            ...
Inconsistent
           Synchronization
public class SomeData {
  private final Map data = new HashMap();

    public void...
Double-checked locking
public class Singleton {
  private static Singleton instance;

    public static Singleton getInsta...
Double-checked locking
public class Singleton {
  private static Singleton instance;

    public static Singleton getInsta...
Double-checked locking
          - volatile
public class Singleton {
  private static volatile Singleton instance;

    pu...
Double-checked locking
     - initialize on demand
public class Singleton {
  private static class SingletonHolder {
    p...
volatile arrays
public final class VolatileArray {

    private volatile boolean[] vals;

    public void flip(int i) {
  ...
Atomicity
Volatile counter
public class Counter {

    private volatile int count;

    public int next() {
      return count++;   ...
AtomicInteger counter
public class Counter {

    private AtomicInteger count =
      new AtomicInteger();

    public int...
Composing atomic actions
public Object putIfAbsent(
  Hashtable table, Object key, Object value) {
         Hashtable is
 ...
Composing atomic actions
public Object putIfAbsent(
  Hashtable table, Object key, Object value) {
         Hashtable is
 ...
Participate in lock
public Object putIfAbsent(
  Hashtable table, Object key, Object value) {
         Hashtable is
      ...
Encapsulated compound
            actions
public Object putIfAbsent(
  ConcurrentHashMap table, Object key, Object value) ...
Assignment of
              64 bit values
public class LongAssignment {

    private long x;

    public void setLong(long...
Assignment of
    64 bit values - volatile
public class LongAssignment {

    private volatile long x;

    public void se...
Safe publication
Listener in constructor
public interface DispatchListener {
  void newFare(Customer customer);
}

public class Taxi
  impl...
Starting thread
             in constructor
public class Cache {

    private final Thread cleanerThread;

    public Cach...
Static factory method
public class Cache {
  // ...

    public static Cache newCache() {
      Cache cache = new Cache();...
Areas of Focus

• Shared Data
• Coordination
• Performance
                 {   • Threads
                     • Wait/noti...
Threads
• THOU SHALT NOT:
 • call Thread.stop() All monitors unlocked by ThreadDeath


 • call Thread.suspend() or Thread.resume()...
wait/notify
// Thread 1
synchronized(lock) { You must synchronize.
  while(! someCondition()) { Always wait in a loop.
   ...
Condition is similar
private final Lock lock = new ReentrantLock();
private final Condition condition = lock.newCondition(...
Areas of Focus

• Shared Data
• Coordination       • Deadlock
• Performance    {   • Spin wait
                     • Thre...
Deadlock
// Thread 1
synchronized(lock1) {
  synchronized(lock2) {
    // stuff
  }
}
                          Classic de...
Deadlock avoidance

• Lock splitting
• Lock ordering
• Lock timeout
• tryLock
Spin wait
// Not efficient
private volatile boolean flag = false;

public void waitTillChange() {
  while(! flag) {
    Th...
Replace with Condition
private final Lock lock = new ReentrantLock();
private final Condition condition = lock.newConditio...
CountDownLatch
private final CountDownLatch latch =
  new CountDownLatch(1);

public void waitTillChange() {
  latch.await...
Lock contention
                         x




                      Hash f(x)




Bucket 0   Bucket 1           Bucket 2 ...
Lock striping
                                 x




                              Hash g(x)




           Hash f(x)     ...
Final Exam
public class StatisticsImpl implements Statistics,
StatisticsImplementor {
  private long queryExecutionCount;
...
Final Exam
public class StatisticsImpl implements Statistics,
StatisticsImplementor {
  private long queryExecutionCount;
...
Thanks...
Twitter       http://twitter.com/puredanger
Blog          http://tech.puredanger.com
Concurrency   http://concur...
Java Concurrency Gotchas
Upcoming SlideShare
Loading in...5
×

Java Concurrency Gotchas

12,734

Published on

Common Java concurrency problems and how to fix them.

Published in: Technology
0 Comments
42 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
12,734
On Slideshare
0
From Embeds
0
Number of Embeds
7
Actions
Shares
0
Downloads
561
Comments
0
Likes
42
Embeds 0
No embeds

No notes for slide

Transcript of "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
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×