Java同步方法锁定对象或方法?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3047564/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 15:59:29  来源:igfitidea点击:

Java synchronized method lock on object, or method?

javamultithreadingthread-safetylockingsynchronized

提问by wuntee

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?

如果我在同一个类中有 2 个同步方法,但每个方法访问不同的变量,2 个线程可以同时访问这 2 个方法吗?锁定发生在对象上,还是像同步方法中的变量一样具体?

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?

2 个线程可以同时访问执行x.addA()的类 X 的同一个实例x.addB()吗?

采纳答案by OscarRyz

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

如果您将方法声明为同步的(就像您通过键入 所做的那样public synchronized void addA()),您将在整个对象上进行同步,因此从同一对象访问不同变量的两个线程无论如何都会相互阻塞。

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 aand bwere object references you would use:

如果你想一次只同步一个变量,这样两个线程在访问不同的变量时不会相互阻塞,你可以在synchronized ()块中分别对它们进行同步。如果ab是对象引用,您将使用:

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 AtomicIntegerinstead:

我建议你改用AtomicInteger

import java.util.concurrent.atomic.AtomicInteger;

class X {

    AtomicInteger a;
    AtomicInteger b;

    public void addA(){
        a.incrementAndGet();
    }

    public void addB(){ 
        b.incrementAndGet();
    }
}

回答by Nathan Hughes

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:

如果您想避免锁定包含对象,您可以选择:

回答by Yishai

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.

我认为,如果 Java 设计者当时知道现在对同步的理解,他们就不会添加语法糖,因为它通常会导致并发的糟糕实现。

回答by dsmith

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.

您可以执行以下操作。在这种情况下,您使用 a 和 b 上的锁进行同步,而不是使用“this”上的锁。我们不能使用 int 因为原始值没有锁,所以我们使用 Integer。

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

回答by Sriharsha g.r.v

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.

这可能不起作用,因为从 Integer 到 int 和反之亦然的装箱和自动装箱取决于 JVM,并且如果两个不同的数字在 -128 和 127 之间,则它们很有可能被散列到相同的地址。

回答by Goyal Vicky

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.

addA 或 addB 方法中只能有一个线程,但同时任意数量的线程都可以进入 changeState 方法。没有两个线程可以同时进入 addA 和 addB(因为对象级锁定)但同时任意数量的线程可以进入 changeState。

回答by Aditya W

From oracle documentation link

来自 oracle 文档链接

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 pageto 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.

这将回答您的问题:在同一个对象 x 上,当同步方法之一正在进行时,您不能同时调用 x.addA() 和 x.addB() 。

回答by Nenad Bulatovic

This example (although not pretty one) can provide more insight into locking mechanism. If incrementAis synchronized, and incrementBis not synchronized, then incrementBwill be executed ASAP, but if incrementBis also synchronizedthen it has to 'wait' for incrementAto finish, before incrementBcan do its job.

这个例子(虽然不是很好)可以提供对锁定机制的更多了解。如果增量型同步的,并incrementB不同步的,那么incrementB会尽快执行,但如果incrementB同步那么它必须“等待”的增量型到结束,前incrementB可以完成自己的工作。

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

这两种方法都被调用到单个实例对象上,在这个例子中是:job,“竞争”线程是aThreadmain

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

尝试在incrementB和没有它的情况下使用 ' synchronized',你会看到不同的结果。如果incrementB也是 ' synchronized' 那么它必须等待incrementA() 完成。每个变体运行几次。

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();
    }
}

回答by MehdiMAH

From "The Java? Tutorials" on synchronizedmethods:

来自同步方法的“Java?教程” :

First, it is not possible for two invocations of synchronized methods on the same objectto 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:

来自同步块的“Java?教程” :

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.

同步语句对于通过细粒度同步提高并发性也很有用。例如,假设类 MsLunch 有两个从未一起使用的实例字段 c1 和 c2。这些字段的所有更新都必须同步,但没有理由阻止 c1 的更新与 c2 的更新交错— 这样做会通过创建不必要的阻塞来减少并发性。我们不使用同步方法或以其他方式使用与此关联的锁,而是创建两个对象来单独提供锁。

(Emphasis mine)

(强调我的)

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

假设您有 2 个非交错变量。因此,您希望同时从不同的线程访问每个线程。您不需要在对象类本身上定义,而是在类Object上定义,如下所示(来自第二个 Oracle 链接的示例):

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++;
        }
    }
}

回答by Jose Velandia

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

有,它会阻止其他方法,因为同步方法应用于WHOLE类对象作为尖....但无论如何它会阻止其他线程执行ONLY,同时执行在任何方法或ADDA ADDB它进入,因为当它完成的总和...一个线程将释放对象,另一个线程将访问其他方法,依此类推。

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).

最后要注意的是,如果有一个 'a' 和 'b' 变量,而不仅仅是一个唯一的变量 'a' 或任何其他名称,则无需同步此方法,因为访问其他变量是完全安全的(其他内存地点)。

class X {

private int a;
private int b;

public void addA(){
    a++;
}

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

Will work as well

也能用