Concurrency 2 Bloch
Concurrency 2 Bloch
Part 3: Concurrency
Concurrency, Part 2
17-214 1
Administrivia
17-214 2
Key concepts from last Tuesday
17-214 3
17-214 4
A concurrency bug with an easy fix
17-214 5
Concurrency control with Java’s intrinsic locks
• synchronized (lock) { … }
– Synchronizes entire block on object lock; cannot forget to unlock
– Intrinsic locks are exclusive: One thread at a time holds the lock
– Intrinsic locks are reentrant: A thread can repeatedly get same lock
• synchronized on an instance method
– Equivalent to synchronized (this) { … } for entire method
• synchronized on a static method in class Foo
– Equivalent to synchronized (Foo.class) { … } for entire method
Thread1
Thread2
Thread3
17-214 6
Another concurrency bug: serial number generation
17-214 7
What went wrong?
17-214 8
Pop quiz – Does this fix work? If not, why not?
17-214 9
It does not – volatile provides only the communications
effects of synchronization (no mutual exclusion)
• But the increment operator (i++) is not atomic
– It’s a read followed by an increment followed by a write
• So you need mutual exclusion
– As provided by Java’s intrinsic locks (synchronized)
– Or an equivalent concurrent abstraction
• As usual, java.util.concurrent is your best bet
– AtomicLong is significantly better than synchronized
– I ran a benchmark : 7.1 ns for AtomicLong vs. 13 ns for synchronized
(Ryzen 9 3900x, 24 Java threads, 24 hyperthreads, 12 cores)
17-214 10
A third concurrency bug: cooperative thread termination
TimeUnit.SECONDS.sleep(5);
stopRequested = true;
}
}
17-214 11
What went wrong?
17-214 12
Pop quiz – what’s wrong with this “fix”?
TimeUnit.SECONDS.sleep(5);
requestStop();
}
}
17-214 13
You must lock write and read!
Otherwise, locking accomplishes nothing
public class StopThread {
private static boolean stopRequested;
private static synchronized void requestStop() {
stopRequested = true;
}
TimeUnit.SECONDS.sleep(1);
requestStop();
}
}
17-214 14
Today
17-214 15
“Fixed” BankAcccount program performs poorly. Why?
Bugs Daffy
Thread Thread
waits-for synchronized(source) {
synchronized(dest) {
source.balance -= amount;
dest.balance += amount;
}
}
17-214 19
Avoiding deadlock
b
d
a e
c
f g
h
i
17-214 20
Avoiding deadlock by ordering lock acquisition
17-214 23
An easy fix: Use a private lock contained in object
17-214 24
An aside: Java Concurrency in Practice annotations
17-214 25
An aside: Java Concurrency in Practice annotations
• For classes
@Immutable
@ThreadSafe
@NotThreadSafe
• For fields
@GuardedBy
17-214 26
Interlude – Ye Olde Puzzler
17-214 27
Puzzler: “Racy Little Number”
import org.junit.Test;
import static org.junit.Assert.assertEquals;
@Test
public void test() throws InterruptedException {
number = 0;
Thread t = new Thread(() -> {
assertEquals(number, 2);
});
number = 1;
t.start();
number++;
t.join();
}
}
17-214 28
How often does this test pass?
import org.junit.Test;
import static org.junit.Assert.assertEquals;
@Test
public void test() throws InterruptedException {
number = 0;
Thread t = new Thread(() -> {
assertEquals(number, 2);
});
number = 1; (a) It always fails
t.start();
number++; (b) It sometimes passes
t.join();
} (c) It always passes
} (d) It always hangs
17-214 29
How often does this test pass?
17-214 30
Another look
import org.junit.*;
import static org.junit.Assert.*;
@Test
public void test() throws InterruptedException {
number = 0;
Thread t = new Thread(() -> {
assertEquals(number, 2); // JUnit never sees exception!
});
number = 1;
t.start();
number++;
t.join();
}
}
17-214 31
How do you fix it? (1)
17-214 32
How do you fix it? (2)
17-214 33
The moral
17-214 34
Puzzler: “Ping Pong”
17-214 35
What does it print?
(a) PingPong
(b) PongPing
(c) It hangs
(c) None of the above
17-214 36
What does it print?
(a) PingPong
(b) PongPing
(c) It hangs
(d) None of the above
17-214 37
Another look
17-214 38
How do you fix it?
17-214 39
The moral
17-214 40
Summary
17-214 41