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

repeats
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());
    }
}
  1. 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));
  2. this.counter ← ⟨SharedCounter$Counter A⟩, this.delta ← 1, this.repeats ← 2

    pass 1 of 2
    20AddTask(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}
  3. 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));
  4. this.counter ← ⟨SharedCounter$Counter A⟩, this.delta ← 10, this.repeats ← 2

    pass 2 of 2
    20AddTask(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}
  5. 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();
  6. plusTen.start();

    40plusOne.start();41plusTen.start();42plusOne.join();
  7. plusTen.start();

    40plusOne.start();41plusTen.start();42plusOne.join();43plusTen.join();
  8. for (int i = 0; i < repeats; i++)

    pass 1 of 4
    27public void run() {28    for (int i0 = 0; i < repeats2; i++) {29        counter.add(delta10);30    }
    All 4 passes — pass 1 is the card above
    passidelta
    1010
    2110
    301
    411
  9. value ← 10

    pass 1 of 4
    5synchronized void add(int delta10) {6    value→ 10 = value + delta10;7    System.out.println("value=" + value10);8}
    outputvalue=10
    All 4 passes — pass 1 is the card above
    passdeltavalue
    1100 10
    21010 20
    3120 21
    4121 22
  10. counter.add(delta);

    28for (int i = 0; i < repeats; i++) {29    counter.add(delta10);30}
  11. counter.add(delta);

    28for (int i = 0; i < repeats; i++) {29    counter.add(delta10);30}
  12. counter.add(delta);

    28for (int i = 0; i < repeats; i++) {29    counter.add(delta1);30}
  13. counter.add(delta);

    28for (int i = 0; i < repeats; i++) {29    counter.add(delta1);30}
  14. plusOne.join();

    41    plusTen.start();42    plusOne.join();43    plusTen.join();4445    System.out.println("final=" + counter.get());46}
  15. int get()

    10int get() {11    return value22;12}
  16. System.out.println("final=" + counter.get());

    45    System.out.println("final=" + counter.get());46}
    outputfinal=22
  1. 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));
  2. this.counter ← ⟨SharedCounter$Counter A⟩, this.delta ← 1, this.repeats ← 1

    pass 1 of 2
    20AddTask(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}
  3. 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));
  4. this.counter ← ⟨SharedCounter$Counter A⟩, this.delta ← 10, this.repeats ← 1

    pass 2 of 2
    20AddTask(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}
  5. 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();
  6. plusTen.start();

    40plusOne.start();41plusTen.start();42plusOne.join();43plusTen.join();
  7. for (int i = 0; i < repeats; i++)

    pass 1 of 2
    27public void run() {28    for (int i0 = 0; i < repeats1; i++) {29        counter.add(delta10);30    }
  8. for (int i = 0; i < repeats; i++)

    pass 2 of 2
    27public void run() {28    for (int i0 = 0; i < repeats1; i++) {29        counter.add(delta1);30    }
  9. value ← 10

    pass 1 of 2
    5synchronized void add(int delta10) {6    value→ 10 = value + delta10;7    System.out.println("value=" + value10);8}
    outputvalue=10
  10. synchronized void add(int delta)

    pass 2 of 2
    5synchronized void add(int delta1) {6    value = value + delta;
  11. 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=11
  12. plusOne.join();

    41    plusTen.start();42    plusOne.join();43    plusTen.join();4445    System.out.println("final=" + counter.get());46}
  13. int get()

    10int get() {11    return value11;12}
  14. System.out.println("final=" + counter.get());

    45    System.out.println("final=" + counter.get());46}
    outputfinal=11
  1. 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));
  2. this.counter ← ⟨SharedCounter$Counter A⟩, this.delta ← 1, this.repeats ← 3

    pass 1 of 2
    20AddTask(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}
  3. 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));
  4. this.counter ← ⟨SharedCounter$Counter A⟩, this.delta ← 10, this.repeats ← 3

    pass 2 of 2
    20AddTask(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}
  5. 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();
  6. plusTen.start();

    40plusOne.start();41plusTen.start();42plusOne.join();43plusTen.join();
  7. for (int i = 0; i < repeats; i++)

    pass 1 of 6
    27public void run() {28    for (int i0 = 0; i < repeats3; i++) {29        counter.add(delta);
    All 6 passes — pass 1 is the card above
    passideltavalue
    10
    2010
    311 101 11
    41111 12
    521
    6210 122 23
  8. value ← 1

    pass 1 of 6
    5synchronized void add(int delta1) {6    value→ 1 = value + delta1;7    System.out.println("value=" + value1);8}
    outputvalue=1
    All 6 passes — pass 1 is the card above
    passdeltavalue
    110 1
    210
    3111
    41012 22
    51
    61023 33
  9. 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        }
  10. delta ← 10

    28for (int i = 0; i < repeats; i++) {29    counter.add(delta→ 10);30}
  11. delta ← 10

    28for (int i = 0; i < repeats; i++) {29    counter.add(delta→ 10);30}
  12. plusOne.join();

    41    plusTen.start();42    plusOne.join();43    plusTen.join();4445    System.out.println("final=" + counter.get());46}
  13. int get()

    10int get() {11    return value33;12}
  14. System.out.println("final=" + counter.get());

    45    System.out.println("final=" + counter.get());46}
    outputfinal=33