我有点困惑。请看一下下面的代码。
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()
是一个静态同步方法,调用非同步静态方法。当线程2获得类级锁时,线程1调用actBusy()
的原因没有被阻止?
我只是在逻辑上感到困惑,如果一个线程得到了类级锁非同步的静态方法仍然是开放的,可以从其他方法(实例方法)调用。为什么?
但是,请注意,您的代码中没有可用的竞争条件,因为竞争条件需要写入,而这些方法中不存在这样的条件。static synchronized
方法在类对象上有一个锁,而非静态synchronized
方法在实例对象
那么为什么来自线程1的CCD_ 8的调用没有被阻止?
由于您的actBusy
方法未同步。即使获得了类级锁,也可以调用非同步的静态方法。
将这些方法标记为synchronized
的目的是启用锁。只有声明为synchronized的方法才受这些锁的约束。因此,如果您获取了一个锁(假设是类级别的锁),那么任何non-synchronized
方法都会像以前一样工作,并且不知道正在获取锁。这允许您决定哪些方法需要被阻止,哪些不需要。
actBusy()
本身并不同步,但调用方方法是同步的。
因此,线程1不会阻塞,因为它获取了this
对象的锁,并且没有其他线程持有this
对象的锁。因此,它可以毫无问题地调用它。
这是因为non-static synchronized
方法锁定当前实例的this
,而不是锁定class
对象。
x.a()
获取当前实例(即x
)上的锁,并且没有其他线程能够进入x
的方法a()
。
线程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()
,而线程1持有类锁,那么线程3将不得不等待,因为该方法也是static-synchornized
。
锁定对象不是分层的。因此,获得类本身的锁不会取代类实例上的锁。它们是单独的锁定对象,只会阻止试图锁定完全相同对象的代码。
因此,如果一个线程进入了静态同步方法,那么将被阻止的线程只有那些试图在同一类上进入静态同步方法的线程。仅仅试图进入非静态同步方法的线程不受影响——它们只是与试图在同一对象实例上进入非静态异步方法的线程竞争。
关于下面的注释,只有标记为synchronized
的静态方法受到类级锁的约束。如果您想阻止其他静态方法,还必须将它们标记为synchronized
。
为什么会出现这种情况?好吧,对于编译器来说,仅仅因为其中一个方法被标记为synchronized
,就认为您需要锁定所有静态方法是相当冒昧的。假设程序员知道必须同步哪些方法才能确保线程安全。