Java中static变量相互引用导致的Bug
来源:互联网 发布:王师就剩一个连 知乎 编辑:程序博客网 时间:2024/04/28 16:25
Bug来源:
这个问题是在实现storm慢请求报警功能时,MailCache类引用了Environments的静态方法。
// class MailCacheprivate static final UrlMap DEFAULT_URLMAP = Environments.getDefaultUrlMap();
在Environments类中,我想在类被初始化时就开一个定时更新cache的定时器,就把它放在了static初始化块中,而updateCache引用了MailCache,因此,MailCache构造函数执行时,它的静态初始化过程还未完成。
public static Map<String, Integer> mRegex2Threshold;public static ScheduledExecutorService mScheduExec; static { updateCache();//updateCache会执行MailCache的静态方法 mScheduExec.scheduleAtFixedRate(new Runnable() { @Override public void run() { updateCache(); } }, 15, 15, TimeUnit.SECONDS);}
Bug简化描述:
在多个类的加载过程中,类加载的顺序是不确定的。当类A引用B时,如果B类还未被加载,则会在本线程中暂停A的执行,去加载B,然后继续执行A,称这个过程是Load_B
类被加载后,一个类或实例的初始化过程是:
step 1、静态变量(初始化块)
step 2、非静态变量(初始化块)
step 3、构造函数
step 2和 step 3的发生必须有new关键字的触发。
如果Load_B发生在A类的step1时,则会造成A的初始化过程被打断,会一起一些问题,比如,A的构造函数会在A静态变量未初始化完成之前被执行!
如下例:
public class B { static { System.out.println("B init"); } static int hook = A.hook; static int bug = 3; static { System.out.println("B other"); } public B() { System.out.println("b bug=" + bug); } public static void main(String... args) { }} public class A { static { System.out.println("A init"); } static int hook; static B b = new B(); static { System.out.println("A other"); }}
当初始化到class A的b变量时,执行class B的构造函数,而此时class B的静态变量还未初始化完毕,也就是构造函数被提前执行了。 由于main方法的存在,class B先被加载,然后执行step 1。当初始化到 hook变量时,发现class A未加载,所以转而加载class A,然后执行A的step 1。
以上程序运行输出:
B initA initb bug=0A otherB other
可以看出,class B构造函数执行时,bug变量的值并不是3,而是int类型的默认值0。
当A和B的逻辑更复杂时,这样的Bug难以定位。造成该Bug的本质原因是A和B存在相互引用的static变量,有以下解决方法:
1、把引用代码,如hook=A.hook放在最下面,即让它成为静态初始化的最后一步。但这样做,代码难以维护,维护者可能会莫名其妙地调到坑里。
2、把涉及相互引用的代码变成非static变量,然后用单例模式保证变量的唯一,改造后的代码:
public class A { static { System.out.println("A init"); } static int hook; B b = new B(); static { System.out.println("A other"); } static A _holder = null; private A() { } public static A getInstance() { if (_holder == null) { synchronized (A.class) { if (_holder == null) { _holder = new A(); } } } return _holder; }}
- Java中static变量相互引用导致的Bug
- Java static变量初始化顺序引发的bug
- java Static的bug
- Java中static final变量的初始化
- Java中static变量的初始化顺序
- java 中static修饰变量的初始值
- Java多线程中static变量的使用
- Java多线程中static变量的使用
- Java中static成员变量的生命周期
- 变量初始化不正确导致的bug
- java中引用变量
- Java mkdir() 导致的Bug
- Java中引用类型变量的转换
- vs2005 bug--static变量
- php中for循环中变量没有清空导致的bug
- 【Java】字符串变量和整形之间的相互转化与静态方法static
- Java中static声明变量
- unity中 字符串与类中GameObject变量的相互引用
- 回首2016-展望2017
- mysql事件详解
- 我的JavaScript学习之路
- 终端启动service和activity
- Java动态代理机制详解(JDK 和CGLIB,Javassist,ASM)
- Java中static变量相互引用导致的Bug
- Usaco2009 gold 修建道路
- win7双显示器如何设置
- IE无法上网,远程计算机或设备将不接受连接,其他浏览器可用
- 让VIM支持Python2 by update-alternatives
- 一次完整的HTTP事务是怎样一个过程?
- 文章标题
- POJ 2635 The Embarrassed Cryptographer (同余问题)
- Cannot proceed with delivery: an existing transporter instance is currently uploading this package