博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【Java】类的循环初始化是否会引起死锁?
阅读量:5735 次
发布时间:2019-06-18

本文共 4498 字,大约阅读时间需要 14 分钟。

  • 前置知识: 类的生命周期

场景设计和推测

  • 情况:

    1. 在类A中的初始化中实例化B
    2. 在类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初始化,造成循环依赖,最终结果为死锁
  • 打点位置:

    1. 类加载结束点(text: Loaded Main2 from file)
    2. 类初始化开始点/结束点(text: Class A2 init)
    3. 实例初始化开始点/结束点(text: Instance A2 init)
    4. 构造函数结束点(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. \ta=" + a + " \ta1=" + a1 + " \ta.b2=" + (a == null ? "NPE" : a.b2) + " \tb=" + B2.b + " \tb1=" + B2.b1 + " \tb.a2=" + (B2.b == null ? "NPE" : B2.b.a2));    }    public int a2 = 11;    static {        a1 = 2;        System.out.println("Class A2 init end. \ta=" + a + " \ta1=" + a1 + " \ta.b2=" + (a == null ? "NPE" : a.b2) + " \tb=" + B2.b + " \tb1=" + B2.b1 + " \tb.a2=" + (B2.b == null ? "NPE" : B2.b.a2));    }    {        a2 = 12;        System.out.println("Instance A2 init end. \ta=" + a + " \ta1=" + a1 + " \ta.b2=" + (a == null ? "NPE" : a.b2) + " \tb=" + B2.b + " \tb1=" + B2.b1 + " \tb.a2=" + (B2.b == null ? "NPE" : B2.b.a2));    }    public A2() {        System.out.println("Instance A2 new. \ta=" + a + " \ta1=" + a1 + " \ta.b2=" + (a == null ? "NPE" : a.b2) + " \tb=" + B2.b + " \tb1=" + B2.b1 + " \tb.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. \tb=" + b + " \tb1=" + b1 + " \tb.a2=" + (b == null ? "NPE" : b.a2) + " \ta=" + A2.a + " \ta1=" + A2.a1 + " \ta.b2=" + (A2.a == null ? "NPE" : A2.a.b2));    }    public int b2 = 21;    static {        b1 = 4;        System.out.println("Class B2 init end. \tb=" + b + " \tb1=" + b1 + " \tb.a2=" + (b == null ? "NPE" : b.a2) + " \ta=" + A2.a + " \ta1=" + A2.a1 + " \ta.b2=" + (A2.a == null ? "NPE" : A2.a.b2));    }    {        b2 = 22;        System.out.println("Instance B2 init end. \tb=" + b + " \tb1=" + b1 + " \tb.a2=" + (b == null ? "NPE" : b.a2) + " \ta=" + A2.a + " \ta1=" + A2.a1 + " \ta.b2=" + (A2.a == null ? "NPE" : A2.a.b2));    }    public B2() {        System.out.println("Instance B2 new. \tb=" + b + " \tb1=" + b1 + " \tb.a2=" + (b == null ? "NPE" : b.a2) + " \ta=" + A2.a + " \ta1=" + A2.a1 + " \ta.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 start4. [Loaded B2 from file:/Users/jiadongy/JVM_Learning_Sample/out/production/JVM_Learning_Sample/]5. Class B2 init start6. Instance A2 init start. a=null a1=0 a.b2=NPE b=null b1=0 b.a2=NPE7. Instance A2 init end.      a=null a1=0 a.b2=NPE b=null b1=0 b.a2=NPE8. Instance A2 new.         a=null a1=0 a.b2=NPE b=null b1=0 b.a2=NPE9. Class B2 init end.              b=A2@61bbe9ba b1=4 b.a2=12 a=null a1=0 a.b2=NPE10. Instance B2 init start.       b=A2@61bbe9ba b1=4 b.a2=12 a=null a1=0 a.b2=NPE11. Instance B2 init end.          b=A2@61bbe9ba b1=4 b.a2=12 a=null a1=0 a.b2=NPE12. Instance B2 new.             b=A2@61bbe9ba b1=4 b.a2=12 a=null a1=0 a.b2=NPE13. Class A2 init end.      a=B2@610455d6 a1=2 a.b2=22 b=A2@61bbe9ba b1=4 b.a2=1214. A2 a=B2@610455d615. A2 a1=216. A2 a2=1217. B2 b=A2@61bbe9ba18. B2 b1=419. 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

总结

  1. 类的循环初始化不会引起死锁
  2. 5个阶段的开始是有顺序的,结束则不一定
  3. 阶段不是排他的/临界的
  4. 循环初始化可能引起意料之外的情况,尽量避免

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

Reference

  1. 深入理解Java虚拟机

转载地址:http://qggwx.baihongyu.com/

你可能感兴趣的文章
Ubuntu 12.04 root用户登录设置
查看>>
存储过程点滴
查看>>
Maven编译跳过test的设置
查看>>
SQLyog图形化l数据库的操作和学习
查看>>
[LeetCode]22.Generate Parentheses
查看>>
计算A/B Test需要的样本量
查看>>
二叉树前序中序后序遍历的非递归方法
查看>>
mysql 行转列列转行
查看>>
《设计模式系列》---桥接模式
查看>>
[Unity3d]Shader 着色器 学习前了解知识
查看>>
Linux中文件颜色所代表的属性和颜色
查看>>
Redrain duilib中事件委托存在的问题
查看>>
43、我的C#学习笔记9
查看>>
网站建表实践及优化
查看>>
字符串的简单操作
查看>>
C#新功能--命名参数与可选参数
查看>>
strtok和strtok_r
查看>>
维辰超市:借助云商城成功转型新零售
查看>>
web.xml中<load-on-start>n</load-on-satrt>作用
查看>>
1061. Dating (20)
查看>>