资讯专栏INFORMATION COLUMN

【Java】类的循环初始化是否会引起死锁?

crelaber / 2745人阅读

摘要:前置知识类的生命周期场景设计和推测情况在类中的初始化中实例化在类的初始化中实例化类设计类静态变量静态变量之后在静态初始化块里赋值为实例变量之后再初始化块中赋值为构造函数类静态变量静态变量之后在静态初始化块里赋值为实例变量之后再初始化块中赋值

前置知识: 类的生命周期

场景设计和推测

情况:

在类A中的初始化中实例化B

在类B的初始化中实例化A

类设计

A类:

静态变量a=new B();静态变量a1=1(之后在静态初始化块里赋值为2);

实例变量a2=11(之后再初始化块中赋值为12);

构造函数;

B类:

静态变量b=new A();静态变量b1=3(之后在静态初始化块里赋值为4);

实例变量b2=21(之后再初始化块中赋值为22);

构造函数;

猜想执行结果: 由于类初始化之后类实例化,所以A类初始化需要B实例化,B实例化又需要A初始化,造成循环依赖,最终结果为死锁

打点位置:

类加载结束点(text: Loaded Main2 from file)

类初始化开始点/结束点(text: Class A2 init)

实例初始化开始点/结束点(text: Instance A2 init)

构造函数结束点(text: Instance A2 new)

场景代码
class A2 {
    static {
        System.out.println("Class A2 init start");
    }

    static B2 a = new B2();
    static int a1 = 1;

    {
        System.out.println("Instance A2 init start. 	a=" + a + " 	a1=" + a1 + " 	a.b2=" + (a == null ? "NPE" : a.b2) + " 	b=" + B2.b + " 	b1=" + B2.b1 + " 	b.a2=" + (B2.b == null ? "NPE" : B2.b.a2));
    }

    public int a2 = 11;

    static {
        a1 = 2;
        System.out.println("Class A2 init end. 	a=" + a + " 	a1=" + a1 + " 	a.b2=" + (a == null ? "NPE" : a.b2) + " 	b=" + B2.b + " 	b1=" + B2.b1 + " 	b.a2=" + (B2.b == null ? "NPE" : B2.b.a2));
    }

    {
        a2 = 12;
        System.out.println("Instance A2 init end. 	a=" + a + " 	a1=" + a1 + " 	a.b2=" + (a == null ? "NPE" : a.b2) + " 	b=" + B2.b + " 	b1=" + B2.b1 + " 	b.a2=" + (B2.b == null ? "NPE" : B2.b.a2));
    }

    public A2() {
        System.out.println("Instance A2 new. 	a=" + a + " 	a1=" + a1 + " 	a.b2=" + (a == null ? "NPE" : a.b2) + " 	b=" + B2.b + " 	b1=" + B2.b1 + " 	b.a2=" + (B2.b == null ? "NPE" : B2.b.a2));
    }
}

class B2 {
    static {
        System.out.println("Class B2 init start");
    }

    static A2 b = new A2();
    static int b1 = 3;

    {
        System.out.println("Instance B2 init start. 	b=" + b + " 	b1=" + b1 + " 	b.a2=" + (b == null ? "NPE" : b.a2) + " 	a=" + A2.a + " 	a1=" + A2.a1 + " 	a.b2=" + (A2.a == null ? "NPE" : A2.a.b2));
    }

    public int b2 = 21;

    static {
        b1 = 4;
        System.out.println("Class B2 init end. 	b=" + b + " 	b1=" + b1 + " 	b.a2=" + (b == null ? "NPE" : b.a2) + " 	a=" + A2.a + " 	a1=" + A2.a1 + " 	a.b2=" + (A2.a == null ? "NPE" : A2.a.b2));
    }

    {
        b2 = 22;
        System.out.println("Instance B2 init end. 	b=" + b + " 	b1=" + b1 + " 	b.a2=" + (b == null ? "NPE" : b.a2) + " 	a=" + A2.a + " 	a1=" + A2.a1 + " 	a.b2=" + (A2.a == null ? "NPE" : A2.a.b2));
    }

    public B2() {
        System.out.println("Instance B2 new. 	b=" + b + " 	b1=" + b1 + " 	b.a2=" + (b == null ? "NPE" : b.a2) + " 	a=" + A2.a + " 	a1=" + A2.a1 + " 	a.b2=" + (A2.a == null ? "NPE" : A2.a.b2));
    }
}

class Main2 {
    static public void main(String... args) {
        System.out.println("A2 a=" + A2.a);
        System.out.println("A2 a1=" + A2.a1);
        System.out.println("A2 a2=" + B2.b.a2);
        System.out.println("B2 b=" + B2.b);
        System.out.println("B2 b1=" + B2.b1);
        System.out.println("B2 b2=" + A2.a.b2);
    }
}
执行结果分析

程序输出结果:

1. [Loaded Main2 from file:/Users/jiadongy/JVM_Learning_Sample/out/production/JVM_Learning_Sample/]
2. [Loaded A2 from file:/Users/jiadongy/JVM_Learning_Sample/out/production/JVM_Learning_Sample/]
3. Class A2 init start
4. [Loaded B2 from file:/Users/jiadongy/JVM_Learning_Sample/out/production/JVM_Learning_Sample/]
5. Class B2 init start
6. Instance A2 init start. a=null a1=0 a.b2=NPE b=null b1=0 b.a2=NPE
7. Instance A2 init end.      a=null a1=0 a.b2=NPE b=null b1=0 b.a2=NPE
8. Instance A2 new.         a=null a1=0 a.b2=NPE b=null b1=0 b.a2=NPE
9. Class B2 init end.              b=A2@61bbe9ba b1=4 b.a2=12 a=null a1=0 a.b2=NPE
10. Instance B2 init start.       b=A2@61bbe9ba b1=4 b.a2=12 a=null a1=0 a.b2=NPE
11. Instance B2 init end.          b=A2@61bbe9ba b1=4 b.a2=12 a=null a1=0 a.b2=NPE
12. Instance B2 new.             b=A2@61bbe9ba b1=4 b.a2=12 a=null a1=0 a.b2=NPE
13. Class A2 init end.      a=B2@610455d6 a1=2 a.b2=22 b=A2@61bbe9ba b1=4 b.a2=12
14. A2 a=B2@610455d6
15. A2 a1=2
16. A2 a2=12
17. B2 b=A2@61bbe9ba
18. B2 b1=4
19. B2 b2=22

把它转化为下面的表格,更加清晰地描述A/B各个阶段执行的过程:

A B
A类加载完成
A类初始化 - 开始
B类加载完成
B类初始化 - 开始
A类实例初始化 - 开始
A类实例初始化 - 结束
A类实例构造函数执行完成
B类初始化 - 结束
B类实例初始化 - 开始
B类实例初始化 - 结束
B类实例构造函数执行完成
A类初始化 - 结束

可以看到在A类初始化的过程中,A类被实例化了(并且该阶段正常结束了),也就是说类的初始化阶段并不是原子的/排他的.
如在本例中,A类实例化阶段的结束早于其类初始化阶段,A类实例化完成时,A类的静态变量还未被初始化.

Reference.1中已经描述了这种情况:

_加载/验证/准备/初始化和卸载这5个阶段的顺序是确定的_,类的加载过程必须按照这种顺序按部就班的开始...注意,这里笔者写的是按部就班的"开始",而不是按部就班的"进行"或"完成",强调这点是因为这些阶段通都是相互交叉混合式进行的,通常会在一个阶段执行的过程中调用/激活另外一个阶段

                                                          <<深入理解Java虚拟机>>P210
总结

类的循环初始化不会引起死锁

5个阶段的开始是有顺序的,结束则不一定

阶段不是排他的/临界的

循环初始化可能引起意料之外的情况,尽量避免

eg.类在初始化过程中修改另一个类的变量,导致另一个类得到了意料之外的初始值

Reference

深入理解Java虚拟机

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/67280.html

相关文章

  • 【面试】Java相关

    摘要:可能会持有相同的值对象但键对象必须是唯一的。当有新任务到达时,线程池没有线程则创建线程处理,处理完成后该线程缓存秒,过期后回收,线程过期前有新任务到达时,则使用缓存的线程来处理。解决死锁问题的三种方法预防死锁检测死锁及避免死锁。 最近辞职准备面试,顺便整理一下面试题分享给大家,如有错误欢迎指出 01. 你对面向对象思想的理解? 面向对象编程简称OOP,是开发程序的一种方法、思想。面向...

    icattlecoder 评论0 收藏0
  • Java面试题

    摘要:近段时间在准备实习的面试,在网上看到一份面试题,就慢慢试着做,争取每天积累一点点。现在每天给自己在面试题编写的任务是题,有时候忙起来可能就没有时间写了,但是争取日更,即使当天没更也会在之后的更新补上。     近段时间在准备实习的面试,在网上看到一份面试题,就慢慢试着做,争取每天积累一点点。    暂时手头上的面试题只有一份,题量还是挺大的,有208题,所以可能讲的不是很详细,只是我自...

    OnlyMyRailgun 评论0 收藏0
  • Java 同步锁

    摘要:如果同一个线程再次请求该锁,计数器会递增,每次占有的线程退出同步代码块时计数器会递减,直至减为时锁才会被释放。表示或在该上的所有线程的个数用来实现重入锁的计数。只有两种可能的值表示没有需要唤醒的线程表示要唤醒一个继任线程来竞争锁。 一、synchronized 1.类型 (1)对象锁 对象锁是作用在实例方法或者一个对象实例上面的 一个类可以有多个实例对象,因此一个类的对象锁可能会有多个...

    xi4oh4o 评论0 收藏0
  • Java 中15种锁的介绍:公平锁,可重入锁,独享锁,互斥锁,乐观锁,分段锁,自旋锁等等

    摘要:公平锁非公平锁公平锁公平锁是指多个线程按照申请锁的顺序来获取锁。加锁后,任何其他试图再次加锁的线程会被阻塞,直到当前进程解锁。重量级锁会让其他申请的线程进入阻塞,性能降低。 Java 中15种锁的介绍 在读很多并发文章中,会提及各种各样锁如公平锁,乐观锁等等,这篇文章介绍各种锁的分类。介绍的内容如下: 公平锁 / 非公平锁 可重入锁 / 不可重入锁 独享锁 / 共享锁 互斥锁 / 读...

    LeoHsiun 评论0 收藏0

发表评论

0条评论

crelaber

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<