views:

285

answers:

4

If I have 2 synchronized methods in the same class, but each accessing different variables, can 2 threads access those 2 methods at the same time? Does the lock occur on the object, or does it get as specific as the variables inside the synchronized method?

Example:

class x{
    private int a;
    private int b;
    public synchronized void addA(){
        a++;
    }
    public synchronized void addB(){
        b++;
    }
}

Can 2 threads access the same instance of class x performing x.addA() and x.addB() at the same time?

+1  A: 

The lock accessed is on the object, not on the method. What variables are accessed within the method is irrelevant.

Adding "synchronized" to the method means the thread running the code must acquire the lock on the object before proceeding. Adding "static synchronized" means the thread running the code must acquire the lock on the class object before proceeding. Alternatively you can wrap code in a block like this:

public void addA() {
    synchronized(this) {
        a++;
    }
}

so that you can specify the object whose lock must be acquired.

If you want to avoid locking on the containing object you can choose between:

  • using synchronized blocks that specify different locks
  • making a and b atomic (using java.util.concurrent.atomic)
Nathan Hughes
+1  A: 

Syncronized on the method declaration is syntactical sugar for this:

 public void addA() {
     syncronized (this) {
          a++;
     }
  }

On a static method it is syntactical sugar for this:

 ClassA {
     public static void addA() {
          syncronized(ClassA.class) {
              a++;
          }
 }

I think if the Java designers knew then what is understood now about syncronization, they would not have added the syntactical sugar, as it more often than not leads to bad implementations of concurrency.

Yishai
Not true. synchronized method generates different bytecode than synchronized(object). While functionality equivalent, it is more than just syntactic sugar.
Steve Kuo
I don't think that "syntactical sugar" is strictly defined as byte-code equivalent. The point is it is functionally equivalent.
Yishai
+4  A: 

If you synchonize on the method ( as you're doing by typing public synchronized void addA() ) you lock the whole object, so two thread accessing a different variable from this same object would block each other anyway.

If you want to syncrhonize only a variable at a time , so two thread wont block each other while accessing different variables, you have to add them in a synchronized block, and use the variable as the "lock" of the block. If a and b were object references you would use:

public void addA() {
    synchronized( a ) {
        a++;
    }
}
public void addB() {
    synchronized( b ) {
        b++;
    }
}

But since they're primitives you can't do this.

I would suggest you to use AtomicInteger instead:

import java.util.concurrent.atomic.AtomicInteger;
class X {
    AtomicInteger a;
    AtomicInteger b;
    public void addA(){
        a.incrementAndGet();
    }
    public void addB(){ 
        b.incrementAndGet();
    }
}
OscarRyz
+1 for suggesting AtomicInteger
Lauri Lehtinen
*If you synchonize on the method you lock the whole object, so two thread accessing a different variable from this same object would block each other anyway.* That's a bit misleading. Synchronizing on the method is functionally equivalent to having a `synchronized (this)` block around the body of the method. The object "this" doesn't become locked, rather the object "this" is used as the mutex and the body is prevented from executing concurrently with other code sections also synchronized on "this." It has no effect on other fields/methods of "this" that aren't synchronized.
Mark Peters
+1  A: 

You can do something like the following. In this case you are using the lock on a and b to synchronized instead of the lock on "this". We cannot use int because primitive values don't have locks, so we use Integer.

class x{
   private Integer a;
   private Integer b;
   public void addA(){
      synchronized(a) {
         a++;
      }
   }
   public synchronized void addB(){
      synchronized(b) {
         b++;
      }
   }
}
dsmith