Reputation: 1967
i have an service witch can be accessed by different threads. The service has 3 longs and two methods - method that adds them all together and method that changes the middle one to 0.
private long a = 10;
private long b = 10;
private long c = 10;
public long add(){
return a + b + c;
}
public void foo(){
b = 0;
}
Now of course the code is more complicated but the point is that while on thread is accessing add() method another thread can access foo() method and change the value of b so im never really sure what the result will be. How can i synchronize these two methods or synchronize the a, b, c variable so this will be thread safe?
Upvotes: 0
Views: 486
Reputation: 314
A way of doing this would be to have another object
say
private object elock = new byte[0];
and then in each of the two methods envelope the critical section with
synchronized(elock) {/* critical section */}
As in
public long add(){
synchronized(elock) {
return a + b + c;
}
}
public void foo(){
synchronized(elock) {
b = 0;
}
}
More optimal locking schemes are available, but since you asked generally, the above should suffice.
Upvotes: 2
Reputation: 27115
You've got four answers so far, all telling you the same thing: to synchronize both methods. But here's the thing: Even with that synchronization, you still will never be sure what the result will be. You've got a data race between the thread that calls add() and the thread that calls foo(). The answer returned by add() will depend on whether it wins or loses the race.
In fact, in this particular example, the synchronization adds no value at all.
With synchronization, you're guaranteed that the foo() method call and the add() method call will not overlap. You can use that knowledge to prove that add() will either return 20 (if it loses the race) or 30 if it wins the race.
But the fact is, in this particular example, because of the way JVMs work, add() always would have returned either 20 or 30 anyway.
If the calculation had more steps, especially if it looped and referred to b more than one time, then the synchronization would matter. With synchronization there would be only two possible answers, but without synchronization, other results could be possible.
Upvotes: 2
Reputation: 3574
You can make the whole thing thread-safe like this:
private long a = 10;
private long b = 10;
private long c = 10;
public synchronized long add(){
return a + b + c;
}
public synchronized void foo(){
b = 0;
}
Marking a non-static method as synchronized
has the following effect:
public synchronized void a() {
// do something
}
// is the same as:
public void a() {
synchronized(this) {
// do something
}
}
And since no two threads can ever lock on the same object at the same time (in this case the object is this
) if one thread callsa()
, then another one calls b()
, the second thread must wait until the first thread has left a()
.
You can learn more about this topic here.
Note that in your example it is not enough to declare only one method as synchronized
, even if the "only-one-thread-at-a-time" behavior is enough for one of the methods. This is because of the Java Memory Model, which is a topic you should have a look at as soon as you know everything else about multithreading in Java.
Upvotes: 1
Reputation: 72844
You can declare the methods as synchronized:
public synchronized long add(){
return a + b + c;
}
public synchronized void foo(){
b = 0;
}
See Synchronized Methods.
Upvotes: 2
Reputation: 58848
If you make the methods synchronized
, then only one will be allowed to execute on the object at any given time. If you have more than one instance of this class, and they are not static, then each instance can have a method executing. In the following example, if add
and foo
are called at the same time, one of them will wait:
public synchronized long add() {
return a + b + c;
}
public synchronized void foo() {
b = 0;
}
Note: Synchronization doesn't compose. That is, combining two synchronized
things is not the same as combining two things then adding synchronized
. Say you had synchronized
methods getA
, getB
, getC
, then this:
public long add2() {
return getA() + getB() + getC();
}
would not be identical to the old add
method, because some other thread might call foo
in between the calls to getA
and getB
.
Upvotes: 4