Java synchronized method lock on object, or method?
Asked Answered
B

11

230

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?

Barretter answered 15/6, 2010 at 17:38 Comment(0)
G
233

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

If you want to synchronize only on one variable at a time, so two threads won't block each other while accessing different variables, you have synchronize on them separately in synchronized () blocks. 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();
    }
}
Gobert answered 15/6, 2010 at 17:48 Comment(6)
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.Specify
Yes, it is truly misleading. For real example - Look at this - #14447595 - Summary : Locking is only at synchronized method level and object's instance variables can be accessed by other threadDamn
The first example is fundamentally broken. If a and b were objects, e.g. Integers, you were synchronizing on instances you are replacing with different objects when applying the ++ operator.Prana
fix your answer and initialize the AtomicInteger: AtomicInteger a = new AtomicInteger(0);Gobioid
Maybe this anwser should be updated with the explained in this other one about synchronizing on the object itself: https://mcmap.net/q/119941/-why-is-it-not-a-good-practice-to-synchronize-on-booleanGriskin
why "synchronized on the method then you synchronize the whole object"?Acadia
C
82

Synchronized on the method declaration is syntactical sugar for this:

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

On a static method it is syntactical sugar for this:

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

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

Concerned answered 15/6, 2010 at 17:45 Comment(4)
Not true. synchronized method generates different bytecode than synchronized(object). While functionality equivalent, it is more than just syntactic sugar.Gurdwara
I don't think that "syntactical sugar" is strictly defined as byte-code equivalent. The point is it is functionally equivalent.Concerned
If the Java designers had known what was already known about monitors they would have/should have done it differently, instead of basically emulating the innards of Unix. Per Brinch Hansen said 'clearly I have laboured in vain' when he saw the Java concurrency primitives.Perspicuous
This is true. The example given by OP would appear to lock each method but in fact they all lock on the same object. Very deceptive syntax. After using Java for 10+ years I didn't know this. So I would avoid synchronized methods for this reason. I always thought that an invisible object was created for each method that was defined with synchronized.Kepner
C
33

From "The Java™ Tutorials" on synchronized methods:

First, it is not possible for two invocations of synchronized methods on the same object to interleave. When one thread is executing a synchronized method for an object, all other threads that invoke synchronized methods for the same object block (suspend execution) until the first thread is done with the object.

From "The Java™ Tutorials" on synchronized blocks:

Synchronized statements are also useful for improving concurrency with fine-grained synchronization. Suppose, for example, class MsLunch has two instance fields, c1 and c2, that are never used together. All updates of these fields must be synchronized, but there's no reason to prevent an update of c1 from being interleaved with an update of c2 — and doing so reduces concurrency by creating unnecessary blocking. Instead of using synchronized methods or otherwise using the lock associated with this, we create two objects solely to provide locks.

(Emphasis mine)

Suppose you have 2 non-interleaving variables. So you want to access to each one from a different threads at the same time. You need to define the lock not on the object class itself, but on the class Object like below (example from the second Oracle link):

public class MsLunch {

    private long c1 = 0;
    private long c2 = 0;

    private Object lock1 = new Object();
    private Object lock2 = new Object();

    public void inc1() {
        synchronized(lock1) {
            c1++;
        }
    }

    public void inc2() {
        synchronized(lock2) {
            c2++;
        }
    }
}
Cicely answered 9/1, 2017 at 9:46 Comment(2)
This works because threads have their own stack, but share instance members, including any declared locking object(s).Amharic
This should be the accepted answer.Jurel
W
15

The lock accessed is on the object, not on the method. Which 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:

Weevily answered 15/6, 2010 at 17:39 Comment(0)
E
7

From oracle documentation link

Making methods synchronized has two effects:

First, it is not possible for two invocations of synchronized methods on the same object to interleave. When one thread is executing a synchronized method for an object, all other threads that invoke synchronized methods for the same object block (suspend execution) until the first thread is done with the object.

Second, when a synchronized method exits, it automatically establishes a happens-before relationship with any subsequent invocation of a synchronized method for the same object. This guarantees that changes to the state of the object are visible to all threads

Have a look at this documentation page to understand intrinsic locks and lock behavior.

This will answer your question: On same object x , you can't call x.addA() and x.addB() at same time when one of the synchronized methods execution is in progress.

Evolute answered 21/2, 2016 at 10:12 Comment(0)
P
5

If you have some methods which are not synchronized and are accessing and changing the instance variables. In your example:

 private int a;
 private int b;

any number of threads can access these non synchronized methods at the same time when other thread is in the synchronized method of the same object and can make changes to instance variables. For e.g :-

 public void changeState() {
      a++;
      b++;
    }

You need to avoid the scenario that non synchronized methods are accessing the instance variables and changing it otherwise there is no point of using synchronized methods.

In the below scenario:-

class X {

        private int a;
        private int b;

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

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

Only one of the threads can be either in addA or addB method but at the same time any number of threads can enter changeState method. No two threads can enter addA and addB at same time(because of Object level locking) but at same time any number of threads can enter changeState.

Printable answered 7/8, 2015 at 10:57 Comment(0)
M
4

This example (although not pretty one) can provide more insight into locking mechanism. If incrementA is synchronized, and incrementB is not synchronized, then incrementB will be executed ASAP, but if incrementB is also synchronized then it has to 'wait' for incrementA to finish, before incrementB can do its job.

Both methods are called onto single instance - object, in this example it is: job, and 'competing' threads are aThread and main.

Try with 'synchronized' in incrementB and without it and you will see different results.If incrementB is 'synchronized' as well then it has to wait for incrementA() to finish. Run several times each variant.

class LockTest implements Runnable {
    int a = 0;
    int b = 0;

    public synchronized void incrementA() {
        for (int i = 0; i < 100; i++) {
            this.a++;
            System.out.println("Thread: " + Thread.currentThread().getName() + "; a: " + this.a);
        }
    }

    // Try with 'synchronized' and without it and you will see different results
    // if incrementB is 'synchronized' as well then it has to wait for incrementA() to finish

    // public void incrementB() {
    public synchronized void incrementB() {
        this.b++;
        System.out.println("*************** incrementB ********************");
        System.out.println("Thread: " + Thread.currentThread().getName() + "; b: " + this.b);
        System.out.println("*************** incrementB ********************");
    }

    @Override
    public void run() {
        incrementA();
        System.out.println("************ incrementA completed *************");
    }
}

class LockTestMain {
    public static void main(String[] args) throws InterruptedException {
        LockTest job = new LockTest();
        Thread aThread = new Thread(job);
        aThread.setName("aThread");
        aThread.start();
        Thread.sleep(1);
        System.out.println("*************** 'main' calling metod: incrementB **********************");
        job.incrementB();
    }
}
Maleate answered 9/12, 2016 at 16:52 Comment(0)
S
3

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++;
      }
   }
}
Surinam answered 15/6, 2010 at 18:5 Comment(0)
C
3

Yes, it will block the other method because synchronized method applies to the WHOLE class object as pointed .... but anyway it will block the other thread execution ONLY while performing the sum in whatever method addA or addB it enters, because when it finish ... the one thread will FREE the object and the other thread will access the other method and so on perfectly working.

I mean the "synchronized" is made precisely for blocking the other thread from accessing another while in a specific code execution. SO FINALLY THIS CODE WILL WORK FINE.

As a final note, if there is an 'a' and 'b' variables, not just an unique variable 'a' or whatever other name, there is no need to synchronize this methods cause it is perfectly safe accesing other var (Other memory location).

class X {

private int a;
private int b;

public void addA(){
    a++;
}

public void addB(){
    b++;
}}

Will work as well

Campus answered 26/5, 2017 at 22:29 Comment(0)
L
3

In Java synchronization,if a thread want to enter into synchronized method/block, it will acquire the lock on:

  • all synchronized non-static methods of this object
    not just on one synchronized method that thread is using
  • all synchronized static methods of this class as well

So the thread which will call the synchronized method addA() for example, will acquire a lock on addA() and addB() as both are synchronized.
So the other threads with same object cannot execute addB().

Leniency answered 20/5, 2019 at 3:2 Comment(0)
A
0

This might not work as the boxing and autoboxing from Integer to int and viceversa is dependant on JVM and there is high possibility that two different numbers might get hashed to same address if they are between -128 and 127.

Amen answered 29/7, 2015 at 21:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.