Kent
Kent

Reputation: 3

Simple synchronization in Java

I was studying the Oracle documentation about the synchronization and there was this class counter :

class Counter {
    int c = 0;

    public void increment() {
        c++;
    }

    public void decrement() {
        c--;
    }

    public int value() {
        return c;
    }
}

And they explain a little about some possible bad interleavings with two Threads, I tried to write a program to test these bad interleavings :

public class TestPara {

    public static void main(String[] args) throws InterruptedException {
        Counter counter = new Counter();
        AdderCounter ac = new AdderCounter(counter);
        SubberCounter sc = new SubberCounter(counter);

        Thread t1 = new Thread(ac);
        Thread t2 = new Thread(sc);

        t1.start();
        t2.start();

        t1.join();
        t2.join();

        System.out.println(counter.c);
    }   
}

class AdderCounter implements Runnable{
    Counter counter;

    public AdderCounter(Counter counter) {
        this.counter = counter;
    }

    @Override
    public void run() {
        counter.increment();
        System.out.println(counter.value());
    }
}

class SubberCounter implements Runnable{
    Counter counter;

    public SubberCounter(Counter counter) {
        this.counter = counter;
    }

    @Override
    public void run() {
        counter.decrement();
        System.out.println(counter.value());
    }
 }

But no matter if the access to c is synchronized or not I always get the values 0, 0 and 0, and I don't know why.

Someone could help me ? Best regards.

Upvotes: 0

Views: 69

Answers (3)

Amongalen
Amongalen

Reputation: 3131

Probably the worst thing about bugs in concurrent programs is that very often they won't show up. You can run the program thousands of times on your local machine and it works just fine. Then you move it to some server which is accessed by millions of ppl and things starts to break "for no reason".

Upvotes: 1

OldCurmudgeon
OldCurmudgeon

Reputation: 65793

You need to run the add/sub many times in parallel.

private static final int COUNT = 1000000;

class Counter {
    int c = 0;

    public void increment() {
        c++;
    }

    public void decrement() {
        c--;
    }

    public int value() {
        return c;
    }
}

class AdderCounter implements Runnable {
    Counter counter;

    public AdderCounter(Counter counter) {
        this.counter = counter;
    }

    @Override
    public void run() {
        for (int i = 0; i < COUNT; i++) {
            counter.increment();
        }
    }
}

class SubberCounter implements Runnable {
    Counter counter;

    public SubberCounter(Counter counter) {
        this.counter = counter;
    }

    @Override
    public void run() {
        for (int i = 0; i < COUNT; i++) {
            counter.decrement();
        }
    }
}

public void test() throws InterruptedException {
    for (int i = 0; i < 10; i++) {
        Counter counter = new Counter();
        AdderCounter ac = new AdderCounter(counter);
        SubberCounter sc = new SubberCounter(counter);

        Thread t1 = new Thread(ac);
        Thread t2 = new Thread(sc);

        t1.start();
        t2.start();

        t1.join();
        t2.join();

        System.out.println(counter.c);
    }
}

For one run this printed:

-33852

28937

-546

-678544

-653775

358351

-593183

1000000 <-- Now that's a REAL coincidence :)

-826735

-773183

for me where you would expect 0 if no thread contention occurred.

BTW: Don't add println statements in your tests - using IO while testing makes interleaving much less likely because most of the time the code will be doing I/O.

Upvotes: 2

xingbin
xingbin

Reputation: 28269

Coincidence, this is the result on my machine:

1
0
0

Process finished with exit code 0

And, if you try a lillte more,

for (int i = 0; i < 100; i++) {
    Counter counter = new Counter();
    AdderCounter ac = new AdderCounter(counter);
    SubberCounter sc = new SubberCounter(counter);
    Thread t1 = new Thread(ac);
    Thread t2 = new Thread(sc);

    t1.start();
    t2.start();

    t1.join();
    t2.join();
    System.out.println(counter.c);
    System.out.println();
}

You may get this:

1
0
0

-1
0
0

1
0
0

1
0
0

1
0
0

-1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

-1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

-1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

-1
0
0

1
0
0

1
0
0

1
0
0

-1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

1
0
0

Upvotes: 2

Related Questions