3

我有一点困惑。请看下面的代码。

public class ThreadDemo {
  //non-static synchronized method
  synchronized void a(){
   actBusy();
  }

  //static synchronized method
  static synchronized void b(){
    actBusy();
  }

  //static method
  static void actBusy(){
    try{
      Thread.sleep(1000);
    }
    catch(InterruptedException e) {
      e.printStackTrace();
    }
  }

  public static void main(String[] args){
    final ThreadDemo x = new ThreadDemo();
    final ThreadDemo y = new ThreadDemo();
    Runnable runnable = new Runnable() {
      public void run() {
         int option = (int) (Math.random() * 4);
         switch (option){
           case 0: x.a();
             break;
           case 1: x.b();
             break;
           case 2: y.b();
             break;
           case 3: y.b();
             break;
         }
      }
    }   ;
    Thread t1 = new Thread(runnable);
    Thread t2 = new Thread(runnable);
    t1.start();
    t2.start();
  }
}

我确定可以调用此序列。

x.a() //in Thread-1
y.b() //in Thread-2

虽然我仍然有一点困惑,但我们可以很容易地看到x.a()调用actBusy()方法,它是一个静态方法。方法b()是调用非同步静态方法的静态同步方法。当thread-2获得类级锁时,为什么actBusy()没有阻塞来自Thread-1的调用?

我只是在逻辑上感到困惑,如果一个线程获得了类级别的锁,那么该类的其他非同步静态方法仍然可以从其他方法(实例方法)调用。为什么?

4

4 回答 4

4

那么为什么actBusy()不阻止来自 Thread-1 的调用呢?

由于您的actBusy方法未同步。即使你获得了类级别的锁,你也可以调用非同步的静态方法。

将方法标记为synchronized是为了启用锁。只有声明为同步的方法才受这些锁的约束。因此,如果您获得了一个锁(假设是类级别的锁),那么任何non-synchronized方法都会像以前一样工作并且不知道正在获得锁。这使您可以决定哪些方法需要被阻止,哪些不需要。

于 2013-08-09T08:36:09.080 回答
3

static synchronized方法在类对象上具有锁,而非静态synchronized方法在实例对象上具有锁( this) - 因此可以同时调用这两种方法,一个线程将运行 1,而另一个线程将运行 2。

但是,请注意,您的代码中没有可用的竞争条件,因为竞争条件需要写入,而这些方法中不存在这种情况。

于 2013-08-09T08:33:23.070 回答
2

actBusy()本身不同步,但调用者方法是同步的。

因此,线程 1 不会阻塞,因为它获取this对象上的锁定并且没有其他线程持有锁定this,因此它能够毫无问题地调用它。

那是因为该non-static synchronized方法锁定在this当前实例上而不是class对象上。

x.a()在当前实例上获取锁,即在当前线程释放锁之前x,没有其他线程能够进入方法a()x

线程 1 -->x.a() //acquires lock and holds it

线程 2 --->x.a() //blocks here until Thread 1 releases lock on x

编辑:

Class Object != Instance 

因此,根据 JMM,它们是不同的对象,两个线程不会相互干扰。所以它允许你调用它。

编辑2:

为什么它允许调用其他静态方法?背后有什么逻辑吗?

假设:

public static synchronized int statefulMethod(){
    //this should be protected
}

public static int nonStatefulMethod(){
    //Just returns a static value such as 5
    //so this is thread safe as it does not have any state
}

public static synchronized int otherStatefulMethod(){
    //this should also be thread safe
}

因此,如果线程 1 处于statefulMethod()具有某些共享状态要保护的方法中,则它使用类级别锁。现在线程 2 调用nonStatefulMethod()它不应该在逻辑上阻塞,因为该方法是线程安全的,并且在这里使该线程阻塞没有意义

现在如果线程 3 调用otherStatefulMethod()while 线程 1 持有类锁,那么线程 3 将不得不等待,因为该方法也是static-synchornized.

于 2013-08-09T08:33:33.133 回答
1

锁定对象不是分层的。因此,获得对类本身的锁定不会取代对类实例的锁定。它们是单独的锁定对象,只会阻止尝试锁定完全相同的对象的代码。

所以如果一个线程进入一个静态同步方法,唯一会被阻塞的线程是那些也试图在同一个类上进入一个静态同步方法的线程。仅尝试进入非静态同步方法的线程不受影响 - 它们仅与尝试在同一对象实例上输入非静态同步方法的线程竞争。

关于您在下面的评论 - 只有标记synchronized的静态方法受类级别锁定。如果您希望阻止其他静态方法,您还必须标记它们synchronized

为什么会这样?好吧,编译器假设您需要锁定所有静态方法只是因为一个标记为synchronized. 假定程序员知道必须同步哪些方法以确保线程安全。

于 2013-08-09T08:39:58.020 回答