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

wun*_*tee 176 java multithreading locking synchronized thread-safety

如果我在同一个类中有2个同步方法,但每个方法访问不同的变量,那么2个线程可以同时访问这两个方法吗?锁是否发生在对象上,或者它是否与synchronized方法中的变量一样具体?

例:

class X {

    private int a;
    private int b;

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

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

}
Run Code Online (Sandbox Code Playgroud)

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

Osc*_*Ryz 181

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

如果你希望只对一个变量在同一时间同步,所以在访问不同的变量两个线程不会阻塞对方,你对他们分别在同步synchronized ()块.如果ab是对象引用,你可以使用:

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

public void addB() {
    synchronized( b ) {
        b++;
    }
}
Run Code Online (Sandbox Code Playgroud)

但由于他们是原始人,你不能这样做.

我建议你改用AtomicInteger:

import java.util.concurrent.atomic.AtomicInteger;

class X {

    AtomicInteger a;
    AtomicInteger b;

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

    public void addB(){ 
        b.incrementAndGet();
    }
}
Run Code Online (Sandbox Code Playgroud)

  • *如果您同步方法锁定整个对象,那么从同一个对象访问不同变量的两个线程无论如何都会相互阻塞.*这有点误导.对方法进行同步在功能上等同于在方法体周围有一个`synchronized(this)`块.对象"this"不会被锁定,而是将对象"this"用作互斥锁,并防止正文与同时在"this"上同步的其他代码段同时执行.它对未同步的"this"的其他字段/方法没有影响. (164认同)
  • 是的,这真的是误导.对于真实的例子 - 看看这个 - http://stackoverflow.com/questions/14447095/does-java-monitor-include-instance-variables - 摘要:锁定仅在同步方法级别,并且对象的实例变量可以被其他人访问线 (12认同)
  • 第一个例子从根本上被打破了.如果`a`和`b`是对象,例如`Integer`s',那么在应用`++`运算符时,您正在使用*替换不同对象*的实例进行同步. (5认同)

Yis*_*hai 66

在方法声明上同步是这样的语法糖:

 public void addA() {
     synchronized (this) {
          a++;
     }
  }
Run Code Online (Sandbox Code Playgroud)

在静态方法上,它是语法糖:

 ClassA {
     public static void addA() {
          synchronized(ClassA.class) {
              a++;
          }
 }
Run Code Online (Sandbox Code Playgroud)

我认为如果Java设计者知道现在对同步的理解,他们就不会添加语法糖,因为它往往会导致并发性的不良实现.

  • 我不认为"语法糖"被严格定义为字节码等价物.关键是它在功能上是等价的. (10认同)
  • 不对.synchronized方法生成与synchronized(对象)不同的字节码.虽然功能相当,但它不仅仅是语法糖. (3认同)
  • 如果 Java 设计者知道“已经”了解关于监视器的内容,他们就会/应该采取不同的做法,而不是基本上模仿 Unix 的内部结构。[Per Brinch Hansen 在看到 Java 并发原语时说“显然我的努力是徒劳的”](https://forums.oracle.com/thread/1142765?start=0&tstart=342)。 (2认同)

小智 18

来自Java SE的同步方法要点:

首先,对同一对象的两个同步方法的调用不可能进行交错.当一个线程正在为对象执行同步方法时,所有其他线程调用同一对象的同步方法(暂停执行)直到第一个线程完成对象.

同步块上Java SE要点:

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

(强调我的.)

你有2个变量no-interleaved.因此,您希望同时访问来自不同线程的每个线程.你需要在对象类本身上定义锁定,而不是像下面的类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++;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


Nat*_*hes 14

访问的锁是在对象上,而不是在方法上.在该方法中访问哪些变量是无关紧要的.

将"synchronized"添加到方法意味着运行代码的线程必须在继续之前获取对象的锁定.添加"静态同步"意味着运行代码的线程必须在继续之前获取类对象的锁定.或者,您可以将代码包装在一个块中,如下所示:

public void addA() {
    synchronized(this) {
        a++;
    }
}
Run Code Online (Sandbox Code Playgroud)

这样您就可以指定必须获取其锁定的对象.

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


Adi*_*a W 6

来自oracle文档链接

使方法同步具有两个效果:

首先,不可能对同一对象的两次同步方法调用进行交织。当一个线程正在执行对象的同步方法时,所有其他线程调用同一对象块的同步方法(挂起执行),直到第一个线程对该对象完成。

其次,当同步方法退出时,它会自动为与同一对象的任何后续同步方法调用建立先发生后关系。这保证了对象状态的更改对所有线程都是可见的

请查看此文档页面以了解固有锁和锁行为。

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


Goy*_*cky 5

如果您有一些未同步的方法并且正在访问和更改实例变量。在你的例子中:

 private int a;
 private int b;
Run Code Online (Sandbox Code Playgroud)

当其他线程处于同一对象的同步方法中并且可以更改实例变量时,任意数量的线程都可以同时访问这些非同步方法。例如:-

 public void changeState() {
      a++;
      b++;
    }
Run Code Online (Sandbox Code Playgroud)

您需要避免非同步方法访问实例变量并更改它的情况,否则没有必要使用同步方法。

在以下情况下:-

class X {

        private int a;
        private int b;

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

        public synchronized void addB(){
            b++;
        }
     public void changeState() {
          a++;
          b++;
        }
    }
Run Code Online (Sandbox Code Playgroud)

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