同步方法是否必须在使用它的 class 外部?

Does a synchronized method have to be external to a class that's using it?

场景一

同步方法是私有的,位于实现 Runnable

的 class 中

Main.java

public class Main { 

Thread thread1 = new Thread(new MyRunnable); 

. . . 

} 

MyRunnable.java

public class MyRunnable implements Runnable { 

. . . 

private synchronized doSomething { 

} 

场景 2.

同步方法是 public 和静态的,位于 Main class

Main.java

  public class Main { 

    Thread thread1 = new Thread(new MyRunnable); 

    public synchronized static doSomething() { 

     }  

    } 

MyRunnable.java

   public class MyRunnable implements Runnable { 

    . . . 


}

提问:以上哪个场景是正确的?

我正在关注场景 2。所以我在 Main class 中有一个同步方法。这很好用。当我将此同步方法移至 MyRunnable class 时,我没有发现任何差异。这很奇怪。我原以为它会失败。 synchronized 阻止同时访问此方法。

但是如果我实例化 class 的两个实例:

MyRunnable runnable1 = new MyRunnable();  
MyRunnable runnable2 = new MyRunnable();  

每个可运行对象都有自己的 synchronized ,而 synchronized 对编译器没有任何意义。我理解正确吗?

synchronized 定义 运行时 行为。它在 编译时 .

什么都不做

一个 synchronized 方法被锁定在对象 (this) 的 实例 上。
static synchronized 方法锁定在对象的 Class 上。

此行为在 JLS §8.4.3.6. synchronized Methods

中定义

A synchronized method acquires a monitor (§17.1) before it executes.

For a class (static) method, the monitor associated with the Class object for the method's class is used.

For an instance method, the monitor associated with this (the object for which the method was invoked) is used.

因此,在您的场景 1 中,方法针对 MyRunnable 的每个单独实例被锁定,而在 场景 2 中该方法被锁定在 Main.class 对象上(它或多或少是全局的,在相同的 class 加载器下也是如此)。

考虑一下,在*场景 1 中:

MyRunnable runnable = new MyRunnable();
Thread t1 = new Thread(runnable);
Thread t2 = new Thread(runnable);

在这里,t1 和 t2 使用相同的 MyRunnable 实例,这意味着它们将无法并行执行 doSomething()

Thread t1 = new Thread(new MyRunnable());
Thread t2 = new Thread(new MyRunnable());

在这里,t1 和 t2 可以在 MyRunnable 的实例上执行 doSomething() 他们被并行地,因为他们锁定了实例。

场景2中,两个线程无法并行执行doSomething(),因为它们锁定在Main.class并且被同一个[=26]加载=].