Java中static变量相互引用导致的Bug

   

Bug来源:

    这个问题是在实现storm慢请求报警功能时,MailCache类引用了Environments的静态方法。

// class MailCache
private 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 init
A init
b bug=0
A other
B 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;
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值