Reputation: 3
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
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
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
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