Java 一个线程能否在另一个线程完成静态初始化(类加载)之前进入静态方法?
假设我们有以下类和两个线程t1,t2Java 一个线程能否在另一个线程完成静态初始化(类加载)之前进入静态方法?,java,multithreading,deadlock,static-initializer,Java,Multithreading,Deadlock,Static Initializer,假设我们有以下类和两个线程t1,t2 public class A { static String str = "abc"; static { B.bMeth(); } static void aMeth() { System.out.println("A::meth()"); str = "abc2"; } static vo
public class A {
static String str = "abc";
static {
B.bMeth();
}
static void aMeth() {
System.out.println("A::meth()");
str = "abc2";
}
static void aSomeMeth() {}
}
public class B {
static {
A.aMeth();
}
static void bMmeth() {}
static void bSomeMeth() {}
}
以下是死锁发生的顺序:
1) t1执行一个.asomeeth(),它在为A加载类时获得锁
2) t2执行B.bSomeMeth(),它在为B加载类时获取锁
3) t1继续执行B.bMeth(),并在保持A的锁的同时要求B的锁
4) t2继续执行A.aMeth(),并要求锁定A,同时保持锁定B
这会导致死锁。但是在我的例子中,t2实际上进入了aMeth
(),并且在访问静态成员str
时被阻止。所以我想知道在特殊条件下,线程在初始化之前是否可能进入静态方法
在我的测试运行中,t2总是像预期的那样在A.amethy()处被阻塞,因此可能会有任何情况下它可以进入并在
str
、JIT优化(如方法内联等)上被阻塞。不,一个类只能由一个线程初始化一次。如果另一个线程访问同一个类,这个线程将在初始化完成时阻塞。对,因此理想情况下t2应该在aMeth
()上阻塞,但是JIT可以为aMeth
()进行方法内联,所以B的静态块看起来像公共类B{static{System.out.println(“A::meth()”);A.str=“abc2”}}因此t2将在访问静态实例的行上被阻塞str
。现在进行一个线程堆栈跟踪,它将在方法aMeth
()中显示t2。不,这不会发生,原因有几个:当加载类时,加载代码仍在被解释。JIT代码不包含任何类加载处理,因为它依赖于所有初始化工作已经完成。否则,它将阻碍许多优化。而且,JIT必须(!)保持代码的相同行为。“一次”误导性地造成了这样一种印象,即这个过程可能不止一次,一次只能发生一个线程。此外,我将“loaded”改为“initialized”,因为加载和初始化是两件不同的事情,不一定由同一个线程完成,问题在于初始化(但这两件事情只能发生一次)。