Reputation: 417
I'm trying to figure out how multiple threads making changes to a single piece of data works. I've recently learned what the synchronized
keyword does, but a certain test is still giving me confusing results.
Here's the entire code:
public class TestSyncTest{
public static void main(String[] args){
TestSync job = new TestSync();
Thread a = new Thread(job);
Thread b = new Thread(job);
a.start();
b.start();
}
}
class TestSync implements Runnable{
private int balance;
public void run(){
for(int i = 0; i < 50; i++){
increment();
System.out.println("balance is " + balance);
}
}
public synchronized void increment(){
int i = balance;
try{
Thread.sleep(10);
} catch(InterruptedException ex){
ex.printStackTrace();
}
balance = i + 1;
}
}
Basically, it's to demonstrate how to make the JVM complete a series of operations on a single piece of data with one thread without other threads getting involved and ruining things. increment()
is synchronized
to make that method a single block for one thread to work on at a time. sleep()
is just to simulate lots of code that takes time at which point the other thread could otherwise jump in. When this code runs, it doesn't matter that sleep()
is there because the entire method is synchronized
. So the output looks nice and clean and correct like this:
>java TestSyncTest
balance is 1
balance is 2
balance is 3
balance is 4
balance is 5
balance is 6
. . .
balance is 98
balance is 99
balance is 100
balance
counts up perfectly as expected. When synchronized
is removed, then this is the output:
>java TestSyncTest
balance is 1
balance is 1
balance is 2
balance is 2
balance is 3
balance is 4
balance is 4
balance is 5
balance is 5
balance is 6
balance is 6
. . .
balance is 48
balance is 48
balance is 49
balance is 49
balance is 50
balance is 50
balance is 51
This is also as expected since the other thread keeps jumping into the middle of the current thread's sensitive increment procedure causing consistent errors. The problem I'm running into is when the increment()
method looks like this:
public synchronized void increment(){
int i = balance;
balance = i + 1;
}
Here, the method is synchronized
, but the second statement inside more or less instantaneously follows the first, and this is one example of the output in its entirety:
>java TestSyncTest
balance is 2
balance is 3
balance is 1
balance is 5
balance is 6
balance is 7
balance is 8
balance is 9
balance is 10
balance is 11
balance is 12
balance is 13
balance is 14
balance is 15
balance is 16
balance is 17
balance is 18
balance is 19
balance is 20
balance is 21
balance is 22
balance is 23
balance is 24
balance is 25
balance is 26
balance is 27
balance is 28
balance is 29
balance is 30
balance is 31
balance is 32
balance is 33
balance is 34
balance is 35
balance is 36
balance is 37
balance is 38
balance is 39
balance is 40
balance is 41
balance is 42
balance is 43
balance is 44
balance is 45
balance is 46
balance is 47
balance is 48
balance is 49
balance is 50
balance is 51
balance is 52
balance is 53
balance is 4
balance is 54
balance is 55
balance is 56
balance is 57
balance is 58
balance is 59
balance is 60
balance is 61
balance is 62
balance is 63
balance is 64
balance is 65
balance is 66
balance is 67
balance is 68
balance is 69
balance is 70
balance is 71
balance is 72
balance is 73
balance is 74
balance is 75
balance is 76
balance is 77
balance is 78
balance is 79
balance is 80
balance is 81
balance is 82
balance is 83
balance is 84
balance is 85
balance is 86
balance is 87
balance is 88
balance is 89
balance is 90
balance is 91
balance is 92
balance is 93
balance is 94
balance is 95
balance is 96
balance is 97
balance is 98
balance is 99
balance is 100
It starts with 2, 1 shows up later in place of 4, and 4 shows up later in between 53 and 54. There's always mix-ups with a few of these numbers, but the end result is always 100, which itself isn't always the last one in the output.
Please help me understand what is going on here.
Upvotes: 3
Views: 77
Reputation: 51483
Even though the method increment()
is synchronized
you still have a race-condition on:
public void run(){
for(int i = 0; i < 50; i++){
increment();
System.out.println("balance is " + balance); // <-- here
}
}
during the read of the variable balance in the System.out.println
. One thread might be modifying that variable in the method increment
() at the same time that another thread is reading that variable.
Notwithstanding, that race-condition is not reflected on your output because the System.out.println
method internally synchronizes
.
In your first code snippet, the threads were outputting the results in order because of the sleep
call. When you removed that call, there is no guarantee that the output produced by System.out.println
will be ordered.
Upvotes: 3