Internet Software Internet Software Development Development Understanding Thread Safety Understanding Thread Safety Paul Krause Paul Krause
Feb 10, 2016
Internet Software Internet Software DevelopmentDevelopment
Understanding Thread SafetyUnderstanding Thread SafetyPaul KrausePaul Krause
Lecture 9Lecture 9
ContentsContents A bit more on LocksA bit more on Locks DeadlockDeadlock Race conditionsRace conditions StarvationStarvation Non-atomic operationsNon-atomic operations
LocksLocks Every object has a lockEvery object has a lock
You lock an object by synchronizing on itYou lock an object by synchronizing on itpublic void addElement(Object item) {public void addElement(Object item) {
synchroniized(myArrayList) {synchroniized(myArrayList) {// do the stuff// do the stuff}}
}}public boolean elementExists(Object item) {public boolean elementExists(Object item) {
return myArrayList.contains(item)return myArrayList.contains(item)}}
To be absolutely accurate, synchronize on To be absolutely accurate, synchronize on access methods tooaccess methods too
What to Lock?What to Lock?public class McBurgerPlace {public class McBurgerPlace {
private static Person ceo = new Person();private static Person ceo = new Person();private Object floor;private Object floor;private Object sodaFountain;private Object sodaFountain;
public static synchronized Person receiveCeo() {public static synchronized Person receiveCeo() {return ceo;return ceo;}}
public synchronized void waxFloor() {// do stuff … }public synchronized void waxFloor() {// do stuff … }
public Object getSodaFountain() {public Object getSodaFountain() {synchronized(sodaFountain) { // do stuff … }synchronized(sodaFountain) { // do stuff … }}}
}}
Non-implicit lockingNon-implicit locking If If threadAthreadA is receiving the is receiving the ceoceo, can , can threadBthreadB wax the floor? wax the floor?
If If threadBthreadB is waxing the floor, can is waxing the floor, can threadCthreadC get the soda fountain? get the soda fountain?
The answer is Yes in both casesThe answer is Yes in both cases The JVM doesn’t do nested lockingThe JVM doesn’t do nested locking
Locking objects not membersLocking objects not memberspublic class LockObjectNotMemberVariables {public class LockObjectNotMemberVariables { private List myList = new ArrayList();private List myList = new ArrayList(); public static void main(String[] args) {public static void main(String[] args) { LockObjectNotMemberVariables lonmv LockObjectNotMemberVariables lonmv
= new LockObjectNotMemberVariables();= new LockObjectNotMemberVariables(); lonmv.lockTest();lonmv.lockTest(); }} public synchronized void lockTest() {public synchronized void lockTest() { System.out.println("Is THIS object locked? " + System.out.println("Is THIS object locked? " +
Thread.holdsLock(this));Thread.holdsLock(this)); System.out.println("Is the list object locked? " +System.out.println("Is the list object locked? " +
Thread.holdsLock(myList));Thread.holdsLock(myList)); } } }}
Locking objects not membersLocking objects not membersinit:init:deps-jar:deps-jar:Created dir: Created dir:
/Users/paulkrause/Java/threading/build/classes/Users/paulkrause/Java/threading/build/classesCompiling 1 source file to Compiling 1 source file to
/Users/paulkrause/Java/threading/build/classes/Users/paulkrause/Java/threading/build/classescompile-single:compile-single:run-single:run-single:Is THIS object locked? trueIs THIS object locked? trueIs the list object locked? falseIs the list object locked? falseBUILD SUCCESSFUL (total time: 4 seconds)BUILD SUCCESSFUL (total time: 4 seconds)
Locking classes not Locking classes not instancesinstances
public class ClassLockNotObjectLock {public class ClassLockNotObjectLock { public static void main(String[] args) {public static void main(String[] args) { lockTest();lockTest(); }} public static synchronized void lockTest() {public static synchronized void lockTest() { ClassLockNotObjectLock clnol ClassLockNotObjectLock clnol
= new ClassLockNotObjectLock();= new ClassLockNotObjectLock(); System.out.println("Is the Class locked? " System.out.println("Is the Class locked? "
+ Thread.holdsLock(clnol.getClass()));+ Thread.holdsLock(clnol.getClass())); System.out.println("Is the object instance System.out.println("Is the object instance
locked? " + Thread.holdsLock(clnol));locked? " + Thread.holdsLock(clnol)); } } }}
Locking classes not Locking classes not instancesinstances
init:init:deps-jar:deps-jar:Compiling 1 source file to Compiling 1 source file to
/Users/paulkrause/Java/threading/build/classes/Users/paulkrause/Java/threading/build/classescompile-single:compile-single:run-single:run-single:Is the Class locked? trueIs the Class locked? trueIs the object instance locked? falseIs the object instance locked? falseBUILD SUCCESSFUL (total time: 4 seconds)BUILD SUCCESSFUL (total time: 4 seconds)
What to lock?What to lock? Synchronising on Objects other than Synchronising on Objects other than
““thisthis” provides more concurrency” provides more concurrency But controlling locks on such a fine scale But controlling locks on such a fine scale
can be inefficient if a method needs to can be inefficient if a method needs to access several objectsaccess several objects
The safest general policy is not to allow The safest general policy is not to allow unsynchronized access to the resources unsynchronized access to the resources you need to lock and synchronize methodsyou need to lock and synchronize methods
Thread Safety ExamplesThread Safety Examples DeadlockDeadlock
First thread gets Lock 1 and then tries to get First thread gets Lock 1 and then tries to get Lock 2Lock 2
Second thread gets Lock 2 and then tries to Second thread gets Lock 2 and then tries to get Lock 1get Lock 1
Race ConditionsRace Conditions Two threads race for a common objectTwo threads race for a common object
StarvationStarvation A thread is never/rarely allowed to executeA thread is never/rarely allowed to execute
DeadlockDeadlockpublic void run() {public void run() {
String name = Thread.currentThread().getName();String name = Thread.currentThread().getName();synchronized(lockA) {synchronized(lockA) {System.out.println(name + ": locked" + lockA);System.out.println(name + ": locked" + lockA);delay(name);delay(name);System.out.println(name + ": trying to get " System.out.println(name + ": trying to get " + lockB);+ lockB);synchronized(lockB) {synchronized(lockB) {System.out.println(name + ": locked" System.out.println(name + ": locked" + lockB);+ lockB);
}} }} }}
Example runExample runinit:init:deps-jar:deps-jar:Compiling 1 source file to Compiling 1 source file to
/Users/paulkrause/Java/threading/build/classes/Users/paulkrause/Java/threading/build/classescompile-single:compile-single:run-single:run-single:Thread-0: lockedLock 1Thread-0: lockedLock 1Thread-0: delaying 1 secondThread-0: delaying 1 secondThread-1: lockedLock 2Thread-1: lockedLock 2Thread-1: delaying 1 secondThread-1: delaying 1 secondThread-0: trying to get Lock 2Thread-0: trying to get Lock 2Thread-1: trying to get Lock 1 Thread-1: trying to get Lock 1 Both threads now blocked Both threads now blocked
Race condition exampleRace condition exampleif (Math.random() > .5) {if (Math.random() > .5) {
peter.start();peter.start();paul.start();paul.start();
} else {} else { paul.start();paul.start(); peter.start();peter.start(); }}
Race condition exampleRace condition examplepublic void run() {public void run() {
System.out.println(getName() + ": trying for lock on " System.out.println(getName() + ": trying for lock on " + server);+ server);synchronized(server) {synchronized(server) {System.out.println(getName() + ": has lock on " System.out.println(getName() + ": has lock on " + server);+ server);// wait 2 seconds: show the other thread is really // wait 2 seconds: show the other thread is really // blocked// blockedtry {try {Thread.sleep(2000);Thread.sleep(2000);} catch (InterruptedException ie) {} catch (InterruptedException ie) {ie.printStackTrace();ie.printStackTrace();}}System.out.println(getName() + System.out.println(getName() + ": releasing lock ");": releasing lock ");
}}
Example run (I)Example run (I)run-single:run-single:Peter: trying for lock on the common objectPeter: trying for lock on the common objectPeter: has lock on the common objectPeter: has lock on the common objectPaul: trying for lock on the common objectPaul: trying for lock on the common objectPeter: releasing lock Peter: releasing lock Paul: has lock on the common objectPaul: has lock on the common objectPaul: releasing lock Paul: releasing lock BUILD SUCCESSFUL (total time: 5 seconds)BUILD SUCCESSFUL (total time: 5 seconds)
Example run (II)Example run (II)run-single:run-single:Paul: trying for lock on the common objectPaul: trying for lock on the common objectPaul: has lock on the common objectPaul: has lock on the common objectPeter: trying for lock on the common objectPeter: trying for lock on the common objectPaul: releasing lock Paul: releasing lock Peter: has lock on the common objectPeter: has lock on the common objectPeter: releasing lock Peter: releasing lock BUILD SUCCESSFUL (total time: 8 seconds)BUILD SUCCESSFUL (total time: 8 seconds)
Starvation ExampleStarvation Examplefor (int i = 0; i < 4; i++) {for (int i = 0; i < 4; i++) {
// create a runner// create a runnerRunner r = new Runner();Runner r = new Runner();r.setPriority(Thread.MAX_PRIORITY);r.setPriority(Thread.MAX_PRIORITY);
// set the first thread to starve// set the first thread to starveif (i == 0) {if (i == 0) {
r.setPriority(Thread.MIN_PRIORITY);r.setPriority(Thread.MIN_PRIORITY);r.setName("Starvation Thread");r.setName("Starvation Thread");
}}// start the thread// start the threadr.start();r.start();r.yield(); // optional liner.yield(); // optional line
}}
Example runExample runrun-single:run-single:Starvation Thread: is working 10Starvation Thread: is working 10Thread-1: is working 10Thread-1: is working 10Thread-2: is working 10Thread-2: is working 10Thread-1: is working 9Thread-1: is working 9Thread-2: is working 9Thread-2: is working 9Thread-1: is working 8Thread-1: is working 8Thread-2: is working 8Thread-2: is working 8Thread-1: is working 7Thread-1: is working 7Thread-2: is working 7Thread-2: is working 7Thread-1: is working 6Thread-1: is working 6Thread-2: is working 6Thread-2: is working 6Thread-1: is working 5Thread-1: is working 5Thread-2: is working 5Thread-2: is working 5Thread-1: is working 4Thread-1: is working 4Thread-2: is working 4Thread-2: is working 4Thread-1: is working 3Thread-1: is working 3Thread-2: is working 3Thread-2: is working 3Thread-1: is working 2Thread-1: is working 2Thread-2: is working 2Thread-2: is working 2Thread-1: is working 1Thread-1: is working 1Thread-2: is working 1Thread-2: is working 1
Thread-3: is working 10Thread-3: is working 10Thread-3: is working 9Thread-3: is working 9Thread-3: is working 8Thread-3: is working 8Thread-3: is working 7Thread-3: is working 7Thread-3: is working 6Thread-3: is working 6Thread-3: is working 5Thread-3: is working 5Thread-3: is working 4Thread-3: is working 4Thread-3: is working 3Thread-3: is working 3Thread-3: is working 2Thread-3: is working 2Thread-3: is working 1Thread-3: is working 1Starvation Thread: is working 9Starvation Thread: is working 9Starvation Thread: is working 8Starvation Thread: is working 8Starvation Thread: is working 7Starvation Thread: is working 7Starvation Thread: is working 6Starvation Thread: is working 6BUILD SUCCESSFUL (total time: 1 second)BUILD SUCCESSFUL (total time: 1 second)
Atomic OperationsAtomic Operations Synchronization blocks some code as Synchronization blocks some code as
“atomic”“atomic” A thread will not be swapped out in the A thread will not be swapped out in the
middle of an atomic operationmiddle of an atomic operation Be careful in assuming any statements are Be careful in assuming any statements are
atomic!atomic!
Non-atomic operationsNon-atomic operations x = 45;x = 45; Is atomic if x is an intIs atomic if x is an int Is not atomic if x is double or longIs not atomic if x is double or long
One operation for the high 32 bits, one for the One operation for the high 32 bits, one for the low 32 bitslow 32 bits
Non-atomic operationsNon-atomic operations TreatTreat
x = 7;x = 7;y = x++;y = x++;
AsAsx = 7;x = 7;int temp = x + 1;int temp = x + 1;x = temp;x = temp;y = x;y = x;
A thread swap herecould lead to unexpected results
Class NonAtomic.javaClass NonAtomic.java Class has a static variableClass has a static variable
static int x;static int x; The start ten threads that each do the following:The start ten threads that each do the following:for (int i= 0; i < 10; i++) {for (int i= 0; i < 10; i++) {
int reference = (int) (Math.random() * 100);int reference = (int) (Math.random() * 100);x = reference; x = reference;
// some calculation to make a slight delay // some calculation to make a slight delay if (x == reference) {if (x == reference) {validCounts++;validCounts++;
} else {} else { invalidCounts++;invalidCounts++;
}}}}
Example runExample runrun-single:run-single:Thread-1 valid: 10 invalid: 0Thread-1 valid: 10 invalid: 0Thread-6 valid: 10 invalid: 0Thread-6 valid: 10 invalid: 0Thread-0 valid: 9 invalid: 1Thread-0 valid: 9 invalid: 1Thread-3 valid: 7 invalid: 3Thread-3 valid: 7 invalid: 3Thread-7 valid: 8 invalid: 2Thread-7 valid: 8 invalid: 2Thread-8 valid: 7 invalid: 3Thread-8 valid: 7 invalid: 3Thread-4 valid: 5 invalid: 5Thread-4 valid: 5 invalid: 5Thread-9 valid: 8 invalid: 2Thread-9 valid: 8 invalid: 2Thread-2 valid: 6 invalid: 4Thread-2 valid: 6 invalid: 4Thread-5 valid: 6 invalid: 4Thread-5 valid: 6 invalid: 4BUILD SUCCESSFUL (total time: 3 seconds)BUILD SUCCESSFUL (total time: 3 seconds)
Problem fixedProblem fixedfor (int i= 0; i < 10; i++) {for (int i= 0; i < 10; i++) {
synchronized(NonAtomic.class) {synchronized(NonAtomic.class) {int reference = (int) (Math.random() * 100);int reference = (int) (Math.random() * 100);x = reference;x = reference;
// Do something intensive here// Do something intensive here
if (x == reference) {if (x == reference) {
validCounts++;validCounts++;} else {} else {
invalidCounts++;invalidCounts++;}}
}}
New runNew runrun-single:run-single:Thread-0 valid: 10 invalid: 0Thread-0 valid: 10 invalid: 0Thread-7 valid: 10 invalid: 0Thread-7 valid: 10 invalid: 0Thread-2 valid: 10 invalid: 0Thread-2 valid: 10 invalid: 0Thread-1 valid: 10 invalid: 0Thread-1 valid: 10 invalid: 0Thread-5 valid: 10 invalid: 0Thread-5 valid: 10 invalid: 0Thread-3 valid: 10 invalid: 0Thread-3 valid: 10 invalid: 0Thread-8 valid: 10 invalid: 0Thread-8 valid: 10 invalid: 0Thread-6 valid: 10 invalid: 0Thread-6 valid: 10 invalid: 0Thread-9 valid: 10 invalid: 0Thread-9 valid: 10 invalid: 0Thread-4 valid: 10 invalid: 0Thread-4 valid: 10 invalid: 0BUILD SUCCESSFUL (total time: 3 seconds)BUILD SUCCESSFUL (total time: 3 seconds)