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