【问题标题】:Can a thread enter a static method before static initialization (class loading) is complete by another thread?一个线程可以在另一个线程完成静态初始化(类加载)之前进入静态方法吗?
【发布时间】:2017-12-11 11:52:23
【问题描述】:

假设我们有以下类和两个线程 t1,t2。

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 执行 A.aSomeMeth() 获取 A 的类加载锁。

2) t2 执行 B.bSomeMeth() 获取 B 的类加载锁。

3) t1 继续执行 B.bMeth() 并在持有 A 的锁的同时要求 B 的锁。

4) t2 继续执行 A.aMeth() 并要求 A 锁定,同时持有 B 锁定。

这会导致死锁。但在我的情况下,t2 实际上进入了aMeth() 并在访问静态成员str 时被阻止。所以我想知道在特殊情况下,线程是否可以在初始化之前进入静态方法。

在我的测试运行中,t2 总是按预期在 A.aMeth() 处被阻止,所以是否有任何极端情况可以进入它并在 str 上被阻止,JIT 优化,如方法内联等。

【问题讨论】:

    标签: java multithreading deadlock static-initializer


    【解决方案1】:

    不,一个类只能由一个线程初始化一次。如果另一个线程访问同一个类,这个类将在初始化完成时阻塞。

    【讨论】:

    • 对,所以理想情况下 t2 应该在 aMeth() 上被阻止,但可以 JIT 为 aMeth() 做方法内联,所以 B 的静态块看起来像 public class B { static { System. out.println("A::meth()"); A.str = "abc2"; } } 等 t2 将在访问静态实例str 的行上被阻止。现在进行线程堆栈跟踪,它会显示 t2 在方法 aMeth() 内。
    • 不,这不可能发生有几个原因:当一个类被加载时,加载代码仍在被解释。 JIT 代码不包含对类加载的任何处理,因为它依赖于所有初始化内容已经完成。否则会阻碍很多优化。此外,JIT 必须 (!) 保持代码的相同行为。
    • 我稍微改写了你的答案。 “一次”会误导性地产生这样的印象,即这个过程可能会发生不止一次,“一次”只有一个线程。此外,我将“已加载”更改为“已初始化”,因为加载和初始化是两件不同的事情,不一定由同一个线程完成,问题是关于初始化(但这两件事只能发生一次)。
    猜你喜欢
    • 1970-01-01
    • 2012-07-26
    • 2011-10-05
    • 2013-09-03
    • 1970-01-01
    • 1970-01-01
    • 2012-04-10
    • 1970-01-01
    • 2012-05-22
    相关资源
    最近更新 更多