Java™ Platform
Concurrency Gotchas


Alex Miller
Terracotta
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
Taxonomy of Concurrency Gotchas

>   Shared Data
>   Coordination
>   Performance




                                  3
Shared Data

>   Locking
>   Visibility
>   Atomicity
>   Safe Publication




                       4
What happens if we modify
  data without locking?




                            5
What happens if we modify
  data without locking?



                   Hint: itʼs not good.




                                          5
6
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
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
Synchronization
private int myField;


synchronized( What goes here? ) {
    myField = 0;
}




                                    9
DO NOT synchronize on null
MyObject obj = null;


synchronized( obj ) {   NullPointerException!
    // stuff
}




                                                10
DO NOT change instance
MyObject obj = new MyObject();


synchronized( obj ) {
                            No longer synchronizing
    obj = new MyObject();    on the same object!
}




                                                      11
12
DO NOT synchronize on string literals
private static final String LOCK = “LOCK”;
synchronized( LOCK ) {
                            What is the scope
    // work                    of LOCK?
}




                                                13
DO NOT synchronize on autoboxed instances
private static final Integer LOCK = 0;
synchronized( LOCK ) {
                          What is the scope
    // work                  of LOCK?
}




                                              14
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
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
Visibility




             17
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
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
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
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
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
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
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
Atomicity




            24
Volatile counter
public class Counter {
    private volatile int count;


    public int next() {
        return count++;   Looks atomic to me!
    }
}




                                                25
AtomicInteger counter

public class Counter {
    private final AtomicInteger count = new AtomicInteger();


    public int next() {
                                          Really atomic by
        return count.getAndIncrement();   encapsulating
    }                                     multiple actions

}




                                                               26
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
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
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
Encapsulated compound actions

public Object putIfAbsent(
    ConcurrentHashMap table, Object key, Object value) {


    table.putIfAbsent(key, value);
}




                                                           29
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
Assignment of 64 bit values - volatile
public class LongAssignment {
    private volatile long x;


    public void setLong(long val) {
        x = val;
    }
}




                                         31
Safe publication




                   Intentionally left blank.




                                               32
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
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
Static factory method
public class Cache {
    // ...


    public static Cache newCache() {
        Cache cache = new Cache();
        cache.startCleanerThread();
        return cache;
    }
}




                                       35
Coordination

>   Threads
>   wait/notify




                  36
Threads

>   DO NOT:
    • Call Thread.stop()
    • Call Thread.suspend() or Thread.resume()
    • Call Thread.destroy()
    • Call Thread.run()
    • Use ThreadGroups



                                                 37
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
Performance

>   Deadlock
>   Spin wait
>   Lock contention




                      39
Deadlock
// Thread 1
synchronized(lock1) {
    synchronized(lock2) {   Classic deadlock.
        // stuff
    }
}


// Thread 2
synchronized(lock2) {
    synchronized(lock1) {
        // stuff
    }
}
                                                40
Deadlock avoidance

>   Lock splitting
>   Lock ordering
>   Lock timeout
>   tryLock




                     41
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
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
Lock contention

                                     x




                                  Hash f(x)




            Bucket 0   Bucket 1           Bucket 2   Bucket 3




                                                                44
Lock striping
                                             x




                                          Hash g(x)




                       Hash f(x)                                 Hash f(x)




            Bucket 0           Bucket 1               Bucket 0           Bucket 1




                                                                                    45
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
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
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
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
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
Alex Miller
Terracotta

Blog:    http://tech.puredanger.com
Twitter: @puredanger

Javaoneconcurrencygotchas 090610192215 Phpapp02

  • 1.
  • 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 ConcurrencyGotchas > Shared Data > Coordination > Performance 3
  • 4.
    Shared Data > Locking > Visibility > Atomicity > Safe Publication 4
  • 5.
    What happens ifwe modify data without locking? 5
  • 6.
    What happens ifwe modify data without locking? Hint: itʼs not good. 5
  • 7.
  • 8.
    Mutable Statics public classMutableStatics { 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 synchronizeon null MyObject obj = null; synchronized( obj ) { NullPointerException! // stuff } 10
  • 12.
    DO NOT changeinstance MyObject obj = new MyObject(); synchronized( obj ) { No longer synchronizing obj = new MyObject(); on the same object! } 11
  • 13.
  • 14.
    DO NOT synchronizeon string literals private static final String LOCK = “LOCK”; synchronized( LOCK ) { What is the scope // work of LOCK? } 13
  • 15.
    DO NOT synchronizeon autoboxed instances private static final Integer LOCK = 0; synchronized( LOCK ) { What is the scope // work of LOCK? } 14
  • 16.
    DO NOT synchronizeon 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 Ilock 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.
  • 19.
    Inconsistent Synchronization public classSomeData { 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 finalclass 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 finalclass 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 finalclass 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 finalclass 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.
  • 27.
    Volatile counter public classCounter { private volatile int count; public int next() { return count++; Looks atomic to me! } } 25
  • 28.
    AtomicInteger counter public classCounter { private final AtomicInteger count = new AtomicInteger(); public int next() { Really atomic by return count.getAndIncrement(); encapsulating } multiple actions } 26
  • 29.
    Composing atomic actions publicObject 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 publicObject 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 publicObject 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 publicObject putIfAbsent( ConcurrentHashMap table, Object key, Object value) { table.putIfAbsent(key, value); } 29
  • 33.
    Assignment of 64bit 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 64bit 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 publicinterface 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 inconstructor 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 publicclass 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 // Notefficient 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 privatefinal 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 classStatisticsImpl 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 classStatisticsImpl 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 classStatisticsImpl 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 classStatisticsImpl 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 classStatisticsImpl 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