Concurrency Basics
Shared Counter
When two threads update shared data, a small synchronized method keeps the update atomic and replay shows the worker calls by thread color.
synchronized
A `synchronized` method lets one thread at a time enter that method for the same object.
shared state
Shared state is data that more than one thread can read or write.
Counter Updates
SharedCounter.java
Replay: real traced execution (multi-file project)
public class SharedCounter {
static class Counter {
private int value;
synchronized void add(int delta) {
value = value + delta;
System.out.println("value=" + value);
}
int get() {
return value;
}
}
static class AddTask implements Runnable {
private final Counter counter;
private final int delta;
private final int repeats;
AddTask(Counter counter, int delta, int repeats) {
this.counter = counter;
this.delta = delta;
this.repeats = repeats;
}
@Override
public void run() {
for (int i = 0; i < repeats; i++) {
counter.add(delta);
}
}
}
public static void main(String[] args) throws InterruptedException {
int repeats = 2;
Counter counter = new Counter();
Thread plusOne = new Thread(new AddTask(counter, 1, repeats));
Thread plusTen = new Thread(new AddTask(counter, 10, repeats));
plusOne.start();
plusTen.start();
plusOne.join();
plusTen.join();
System.out.println("final=" + counter.get());
}
}
public class SharedCounter {
static class Counter {
private int value;
synchronized void add(int delta) {
value = value + delta;
System.out.println("value=" + value);
}
int get() {
return value;
}
}
static class AddTask implements Runnable {
private final Counter counter;
private final int delta;
private final int repeats;
AddTask(Counter counter, int delta, int repeats) {
this.counter = counter;
this.delta = delta;
this.repeats = repeats;
}
@Override
public void run() {
for (int i = 0; i < repeats; i++) {
counter.add(delta);
}
}
}
public static void main(String[] args) throws InterruptedException {
int repeats = 1;
Counter counter = new Counter();
Thread plusOne = new Thread(new AddTask(counter, 1, repeats));
Thread plusTen = new Thread(new AddTask(counter, 10, repeats));
plusOne.start();
plusTen.start();
plusOne.join();
plusTen.join();
System.out.println("final=" + counter.get());
}
}
public class SharedCounter {
static class Counter {
private int value;
synchronized void add(int delta) {
value = value + delta;
System.out.println("value=" + value);
}
int get() {
return value;
}
}
static class AddTask implements Runnable {
private final Counter counter;
private final int delta;
private final int repeats;
AddTask(Counter counter, int delta, int repeats) {
this.counter = counter;
this.delta = delta;
this.repeats = repeats;
}
@Override
public void run() {
for (int i = 0; i < repeats; i++) {
counter.add(delta);
}
}
}
public static void main(String[] args) throws InterruptedException {
int repeats = 3;
Counter counter = new Counter();
Thread plusOne = new Thread(new AddTask(counter, 1, repeats));
Thread plusTen = new Thread(new AddTask(counter, 10, repeats));
plusOne.start();
plusTen.start();
plusOne.join();
plusTen.join();
System.out.println("final=" + counter.get());
}
}
repeats ← 2, counter ← ⟨SharedCounter$Counter A⟩
34public static void main(String[] args) throws InterruptedException {35 int repeats→ 2 = 2; //@repeats=1, 336 Counter counter→ ⟨SharedCounter$Counter A⟩ = new Counter();37 Thread plusOne = new Thread(new AddTask(counter, 1, repeats));38 Thread plusTen = new Thread(new AddTask(counter, 10, repeats));this.counter ← ⟨SharedCounter$Counter A⟩, this.delta ← 1, this.repeats ← 2
pass 1 of 220AddTask(Counter counter⟨SharedCounter$Counter A⟩, int delta1, int repeats2) {21 this.counter→ ⟨SharedCounter$Counter A⟩ = counter⟨SharedCounter$Counter A⟩;22 this.delta→ 1 = delta1;23 this.repeats→ 2 = repeats2;24}plusOne ← Thread[#19,Thread-0,5,main]
36Counter counter = new Counter();37Thread plusOne→ Thread[#19,Thread-0,5,main] = new Thread(new AddTask(counter, 1, repeats));38Thread plusTen = new Thread(new AddTask(counter, 10, repeats));this.counter ← ⟨SharedCounter$Counter A⟩, this.delta ← 10, this.repeats ← 2
pass 2 of 220AddTask(Counter counter⟨SharedCounter$Counter A⟩, int delta10, int repeats2) {21 this.counter→ ⟨SharedCounter$Counter A⟩ = counter⟨SharedCounter$Counter A⟩;22 this.delta→ 10 = delta10;23 this.repeats→ 2 = repeats2;24}plusTen ← Thread[#20,Thread-1,5,main]
37Thread plusOne = new Thread(new AddTask(counter, 1, repeats));38Thread plusTen→ Thread[#20,Thread-1,5,main] = new Thread(new AddTask(counter, 10, repeats));3940plusOne.start();41plusTen.start();plusTen.start();
40plusOne.start();41plusTen.start();42plusOne.join();plusTen.start();
40plusOne.start();41plusTen.start();42plusOne.join();43plusTen.join();for (int i = 0; i < repeats; i++)
pass 1 of 427public void run() {28 for (int i0 = 0; i < repeats2; i++) {29 counter.add(delta10);30 }All 4 passes — pass 1 is the card above pass idelta1 0 10 2 1 10 3 0 1 4 1 1 value ← 10
pass 1 of 45synchronized void add(int delta10) {6 value→ 10 = value + delta10;7 System.out.println("value=" + value10);8}outputvalue=10All 4 passes — pass 1 is the card above pass deltavalue1 10 0 → 10 2 10 10 → 20 3 1 20 → 21 4 1 21 → 22 counter.add(delta);
28for (int i = 0; i < repeats; i++) {29 counter.add(delta10);30}counter.add(delta);
28for (int i = 0; i < repeats; i++) {29 counter.add(delta10);30}counter.add(delta);
28for (int i = 0; i < repeats; i++) {29 counter.add(delta1);30}counter.add(delta);
28for (int i = 0; i < repeats; i++) {29 counter.add(delta1);30}plusOne.join();
41 plusTen.start();42 plusOne.join();43 plusTen.join();4445 System.out.println("final=" + counter.get());46}int get()
10int get() {11 return value22;12}System.out.println("final=" + counter.get());
45 System.out.println("final=" + counter.get());46}outputfinal=22
repeats ← 1, counter ← ⟨SharedCounter$Counter A⟩
34public static void main(String[] args) throws InterruptedException {35 int repeats→ 1 = 1;36 Counter counter→ ⟨SharedCounter$Counter A⟩ = new Counter();37 Thread plusOne = new Thread(new AddTask(counter, 1, repeats));38 Thread plusTen = new Thread(new AddTask(counter, 10, repeats));this.counter ← ⟨SharedCounter$Counter A⟩, this.delta ← 1, this.repeats ← 1
pass 1 of 220AddTask(Counter counter⟨SharedCounter$Counter A⟩, int delta1, int repeats1) {21 this.counter→ ⟨SharedCounter$Counter A⟩ = counter⟨SharedCounter$Counter A⟩;22 this.delta→ 1 = delta1;23 this.repeats→ 1 = repeats1;24}plusOne ← Thread[#19,Thread-0,5,main]
36Counter counter = new Counter();37Thread plusOne→ Thread[#19,Thread-0,5,main] = new Thread(new AddTask(counter, 1, repeats));38Thread plusTen = new Thread(new AddTask(counter, 10, repeats));this.counter ← ⟨SharedCounter$Counter A⟩, this.delta ← 10, this.repeats ← 1
pass 2 of 220AddTask(Counter counter⟨SharedCounter$Counter A⟩, int delta10, int repeats1) {21 this.counter→ ⟨SharedCounter$Counter A⟩ = counter⟨SharedCounter$Counter A⟩;22 this.delta→ 10 = delta10;23 this.repeats→ 1 = repeats1;24}plusTen ← Thread[#20,Thread-1,5,main]
37Thread plusOne = new Thread(new AddTask(counter, 1, repeats));38Thread plusTen→ Thread[#20,Thread-1,5,main] = new Thread(new AddTask(counter, 10, repeats));3940plusOne.start();41plusTen.start();42plusOne.join();plusTen.start();
40plusOne.start();41plusTen.start();42plusOne.join();43plusTen.join();for (int i = 0; i < repeats; i++)
pass 1 of 227public void run() {28 for (int i0 = 0; i < repeats1; i++) {29 counter.add(delta10);30 }for (int i = 0; i < repeats; i++)
pass 2 of 227public void run() {28 for (int i0 = 0; i < repeats1; i++) {29 counter.add(delta1);30 }value ← 10
pass 1 of 25synchronized void add(int delta10) {6 value→ 10 = value + delta10;7 System.out.println("value=" + value10);8}outputvalue=10synchronized void add(int delta)
pass 2 of 25synchronized void add(int delta1) {6 value = value + delta;delta ← 1, value ← 11
5 synchronized void add(int delta) {6 value→ 11 = value + delta1;7 System.out.println("value=" + value11);8 }910 int get() {11 return value;12 }13}1415static class AddTask implements Runnable {16 private final Counter counter;17 private final int delta;18 private final int repeats;1920 AddTask(Counter counter, int delta, int repeats) {21 this.counter = counter;22 this.delta = delta;23 this.repeats = repeats;24 }2526 @Override27 public void run() {28 for (int i = 0; i < repeats; i++) {29 counter.add(delta→ 1);30 }outputvalue=11plusOne.join();
41 plusTen.start();42 plusOne.join();43 plusTen.join();4445 System.out.println("final=" + counter.get());46}int get()
10int get() {11 return value11;12}System.out.println("final=" + counter.get());
45 System.out.println("final=" + counter.get());46}outputfinal=11
repeats ← 3, counter ← ⟨SharedCounter$Counter A⟩
34public static void main(String[] args) throws InterruptedException {35 int repeats→ 3 = 3;36 Counter counter→ ⟨SharedCounter$Counter A⟩ = new Counter();37 Thread plusOne = new Thread(new AddTask(counter, 1, repeats));38 Thread plusTen = new Thread(new AddTask(counter, 10, repeats));this.counter ← ⟨SharedCounter$Counter A⟩, this.delta ← 1, this.repeats ← 3
pass 1 of 220AddTask(Counter counter⟨SharedCounter$Counter A⟩, int delta1, int repeats3) {21 this.counter→ ⟨SharedCounter$Counter A⟩ = counter⟨SharedCounter$Counter A⟩;22 this.delta→ 1 = delta1;23 this.repeats→ 3 = repeats3;24}plusOne ← Thread[#19,Thread-0,5,main]
36Counter counter = new Counter();37Thread plusOne→ Thread[#19,Thread-0,5,main] = new Thread(new AddTask(counter, 1, repeats));38Thread plusTen = new Thread(new AddTask(counter, 10, repeats));this.counter ← ⟨SharedCounter$Counter A⟩, this.delta ← 10, this.repeats ← 3
pass 2 of 220AddTask(Counter counter⟨SharedCounter$Counter A⟩, int delta10, int repeats3) {21 this.counter→ ⟨SharedCounter$Counter A⟩ = counter⟨SharedCounter$Counter A⟩;22 this.delta→ 10 = delta10;23 this.repeats→ 3 = repeats3;24}plusTen ← Thread[#20,Thread-1,5,main]
37Thread plusOne = new Thread(new AddTask(counter, 1, repeats));38Thread plusTen→ Thread[#20,Thread-1,5,main] = new Thread(new AddTask(counter, 10, repeats));3940plusOne.start();41plusTen.start();42plusOne.join();plusTen.start();
40plusOne.start();41plusTen.start();42plusOne.join();43plusTen.join();for (int i = 0; i < repeats; i++)
pass 1 of 627public void run() {28 for (int i0 = 0; i < repeats3; i++) {29 counter.add(delta);All 6 passes — pass 1 is the card above pass ideltavalue1 0 — — 2 0 10 — 3 1 1 → 10 1 → 11 4 1 1 11 → 12 5 2 1 — 6 2 10 → 1 22 → 23 value ← 1
pass 1 of 65synchronized void add(int delta1) {6 value→ 1 = value + delta1;7 System.out.println("value=" + value1);8}outputvalue=1All 6 passes — pass 1 is the card above pass deltavalue1 1 0 → 1 2 10 — 3 1 11 4 10 12 → 22 5 1 — 6 10 23 → 33 delta ← 1
5 synchronized void add(int delta) {6 value = value1 + delta10;7 System.out.println("value=" + value);8 }910 int get() {11 return value;12 }13}1415static class AddTask implements Runnable {16 private final Counter counter;17 private final int delta;18 private final int repeats;1920 AddTask(Counter counter, int delta, int repeats) {21 this.counter = counter;22 this.delta = delta;23 this.repeats = repeats;24 }2526 @Override27 public void run() {28 for (int i = 0; i < repeats; i++) {29 counter.add(delta→ 1);30 }delta ← 10
28for (int i = 0; i < repeats; i++) {29 counter.add(delta→ 10);30}delta ← 10
28for (int i = 0; i < repeats; i++) {29 counter.add(delta→ 10);30}plusOne.join();
41 plusTen.start();42 plusOne.join();43 plusTen.join();4445 System.out.println("final=" + counter.get());46}int get()
10int get() {11 return value33;12}System.out.println("final=" + counter.get());
45 System.out.println("final=" + counter.get());46}outputfinal=33