user1985273
user1985273

Reputation: 1967

Synchronize two different methods with eachother or synchronize variables

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

Answers (5)

Shripathi Kamath
Shripathi Kamath

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

Solomon Slow
Solomon Slow

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

MinecraftShamrock
MinecraftShamrock

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

M A
M A

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

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

Related Questions