简体   繁体   English

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

[英]Java synchronized method lock on object, or method?

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? 如果我在同一个类中有2个同步方法,但每个方法访问不同的变量,那么2个线程可以同时访问这两个方法吗? Does the lock occur on the object, or does it get as specific as the variables inside the synchronized method? 锁是否发生在对象上,或者它是否与synchronized方法中的变量一样具体?

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类实例,执行x.addA( )和x.addB()吗?

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. 如果您将方法声明为synchronized (正如您通过键入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. 如果您希望一次仅同步一个变量,那么两个线程在访问不同变量时不会相互阻塞,您可以在synchronized ()块中单独synchronized ()它们。 If a and b were object references you would use: 如果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 AtomicInteger instead: 我建议你改用AtomicInteger

import java.util.concurrent.atomic.AtomicInteger;

class X {

    AtomicInteger a;
    AtomicInteger b;

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

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

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设计者知道现在对同步的理解,他们就不会添加语法糖,因为它往往会导致并发性的不良实现。

From "The Java™ Tutorials" on synchronized methods : 来自同步方法的 “The Java™Tutorials”:

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 : 同步块上的“The Java™Tutorials”:

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. 例如,假设类MsLunch有两个从不一起使用的实例字段c1和c2。 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. 必须同步这些字段的所有更新, 但没有理由阻止c1的更新与c2的更新交错 - 这样做会通过创建不必要的阻塞来减少并发性。 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. 假设您有2个非交错变量。 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): 您需要在对象类本身上定义锁定,而不是在类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++;
        }
    }
}

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. 将“synchronized”添加到方法意味着运行代码的线程必须在继续之前获取对象的锁定。 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: 如果要避免锁定包含对象,可以选择:

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 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. 这将回答您的问题:在同一个对象x上,当其中一个同步方法执行正在进行时,您无法同时调用x.addA()和x.addB()。

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". 在这种情况下,您使用a和b上的锁来同步而不是锁定“this”。 We cannot use int because primitive values don't have locks, so we use Integer. 我们不能使用int,因为原始值没有锁,所以我们使用Integer。

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

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. 当其他线程在同一对象的synchronized方法中并且可以对实例变量进行更改时,任意数量的线程都可以同时访问这些非同步方法。 For eg :- 例如: -

 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. 只有一个线程可以是addA或addB方法,但同时任何数量的线程都可以进入changeState方法。 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。

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. 如果incrementA同步的 ,并且incrementB 不同步 ,则incrementB将在ASAP执行,但如果incrementB也是同步的,那么它必须“等待” incrementA完成,然后incrementB才能完成它的工作。

Both methods are called onto single instance - object, in this example it is: job , and 'competing' threads are aThread and main . 这两种方法都被调用到单个实例 - 对象,在这个例子中它是: job ,而'竞争'线程是aThreadmain

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. 尝试使用incrementB中的 ' synchronized '而不使用它,你会看到不同的结果。如果incrementB也是' synchronized ',那么它必须等待incrementA ()完成。 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();
    }
}

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. 是的,它会阻止其他方法,因为同步方法适用于作为指出整个类对象....但无论如何它会阻止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'或其他任何名称,则无需同步此方法,因为它可以非常安全地访问其他var(其他内存)地点)。

class X {

private int a;
private int b;

public void addA(){
    a++;
}

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

Will work as well 也会工作

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

In java synchronization,if a thread want to enter into synchronization method it will acquire lock on all synchronized methods of that object not just on one synchronized method that thread is using. 在java同步中,如果线程想要进入同步方法,它将获取该对象的所有同步方法的锁定,而不仅仅是在线程正在使用的一个同步方法上。 So a thread executing addA() will acquire lock on addA() and addB() as both are synchronized.So other threads with same object cannot execute addB(). 因此,执行addA()的线程将获取对addA()和addB()的锁定,因为两者都是同步的。因此具有相同对象的其他线程无法执行addB()。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM