1. 程式人生 > >Java中static變數相互引用導致的Bug

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;
    }
}


相關推薦

Javastatic變數相互引用導致Bug

Bug來源:     這個問題是在實現storm慢請求報警功能時,MailCache類引用了Environments的靜態方法。 // class MailCache private static final UrlMap DEFAULT_URLMAP = Envir

Javastatic變數作用和用法詳解

static表示“全域性”或者“靜態”的意思,用來修飾成員變數和成員方法,也可以形成靜態static程式碼塊,但是Java語言中沒有全域性變數的概念。 被static修飾的成員變數和成員方法獨立於該類的任何物件。也就是說,它不依賴類特定的例項,被類的所有例項共

Javastatic(靜態變數/方法)的優缺點

static關鍵字宣告的變數或方法稱為靜態變數/方法 靜態static變數/方法在類載入的過程中被初始化,在記憶體中只存在一份,所以可以把它當作是全域性變數/方法。 優點 屬於類級別的,不需要建立物件就可以直接使用. 全域性唯一,記憶體中唯一,靜態變數

javastatic關鍵字的使用--靜態成員變數

一、修飾成員變數 沒有被static修飾的成員變數,叫做例項變數。 被static修飾的成員變數,稱為靜態成員變數,也叫做類變數 特點: 所有物件共享靜態成員變數。 可以使用類名直接呼叫。(使用物件名仍能呼叫) 二、使用 package com.key

靜態變數未定義導致undefined reference to static class member問題的解決方法

undefined reference to ***這個連結錯誤的花樣總是層出不窮(more),這一次是找不到類中的成員。例子1:undefined reference to VS. 類靜態成員變數在檔案A.h中聲明瞭類A與類B:class A{ friend class

Java多執行緒static變數的使用 SimpleDateFormat時間格式化存線上程安全問題

兩篇文章 Java多執行緒中static變數的使用  (轉自:http://blog.csdn.net/yy304935305/article/details/52456771) &&  SimpleDateFormat時間格式化存線上程安全問題

Javastatic關鍵字用法總結

副本 大括號 跟著 rac clas main 靜態成員變量 abstract 全局變量 1. 靜態方法 通常,在一個類中定義一個方法為static,那就是說,無需本類的對象即可調用此方法 聲明為static的方法有以下幾條限制: · 它們僅能調用其他的sta

java值傳遞和引用傳遞

public 例子 oar 是什麽 sta light 修改 [] 重要 1:按值傳遞是什麽 指的是在方法調用時,傳遞的參數是按值的拷貝傳遞。示例如下: [java] view plain copy public class TempTest { private

關於java值傳遞還是引用傳遞

jvm 文章 log 關於 操作 eap 數據 heap 但是 網上有很多示例,我比較懶,就不寫示例了,寫寫自己的感受。 java中是值傳遞還是引用傳遞,我認為不是很重要,重要的是對於傳遞的理解。 方法中的參數分為兩種 基本數據類型 引用數據類型(類、接口、數組)

Javastatic問題

criteria ndis iter 對象 ron logs 變量定義 加載 會有 一定要清楚static,如果胡亂使用極易造成各種錯誤。 (1)比如:如果定義某個全局變量a是static的,當多次測試代碼時,前一次運行測試得到a的值對下一次測試結果會有影響,這樣的話就造成

轉:javastatic、final、static final的區別

重寫 一個 修改 表示 指針 子類 不同 常數 ati http://blog.csdn.net/qq1623267754/article/details/36190715 final可以修飾:屬性,方法,類,局部變量(方法中的變量) final修飾的屬性的初始化

Java的四種引用

hab clear 不足 生命周期 特點 目的 bject 存在 增強   在JDK 1.2以前的版本中,若一個對象不被任何變量引用,那麽程序就無法再使用這個對象。也就是說,只有對象處於可觸及(reachable)狀態,程序才能使用它。從JDK 1.2版本開始,把對象的引用

IDEAmodule之間相互引用,必須要install到本地的解決方案

臺電腦 有一個 dea alt 神馬 分享圖片 技術分享 module 引擎 CSDN昨晚打不開了,搞了好久也沒弄明白,很多搜索引擎查到的方案都試過了,都有問題,我用的是idea2018-1旗艦版。 這個問題現在依舊沒有解決,但是我另一臺電腦上有一個2016版本,裝過了之

Javastatic關鍵字解析

地方 通過 特性 inf 優化 href compare 筆試 star Java中的static關鍵字解析   static關鍵字是很多朋友在編寫代碼和閱讀代碼時碰到的比較難以理解的一個關鍵字,也是各大公司的面試官喜歡在面試時問到的知識點之一。下面就先講述一下static

Java成員變數、區域性變數、全域性變數、靜態變數存在位置及初始化

根據定義變數位置的不同,可以將變數分為成員變數和區域性變數。 成員變數是 定義在一個類的變數:類中方法外 區域性變數是定義在一個方法內的變數:方法中 成員變數分為: 靜態屬性:隨類的存在而存在,是在類載入的初始化 非靜態屬性:隨例項的屬性存在而存在。 區域性變數: 區域性變數不能加s

java物件和物件引用的區別

1.何謂物件?   在Java中有一句比較流行的話,叫做“萬物皆物件”,這是Java語言設計之初的理念之一。要理解什麼是物件,需要跟類一起結合起來理解。下面這段話引自《Java程式設計思想》中的一段原話:   “按照通俗的說法,每個物件都是某個類(class)的一個例項(instance),這裡,‘

java成員變數和區域性變數

1.成員變數   在類中定義,用來描述物件將要有什麼。 2.區域性變數 在類的方法中定義,在方法中臨時儲存資料 成員變數和區域性變數區別 1.作用域不同 成員變數的作用域在整個類內部都是可見的 區域性變數的作用僅限於定義它的方法、 2.初始值不同 jav

Java物件的this引用

        Java提供了一個this關鍵字,this關鍵字總是指向呼叫該方法的物件。根據this出現位置的不同,this作為物件的預設引用有兩種情形。 -構造器中引用該構造器正在初始化的物件 -在方法中引用呼叫該方法的物件   &n

JAVA基礎(7)---java變數和常量

資料在執行過程中,有的不會改變,有的會發生改變。變得就稱為變數  不變的就稱常量。 變數 變數:變數是Java程式中最基本的儲存單元  變數的屬性:變數名,資料型別,儲存單元和變數值 1、變數名:合法的識別符號 2、資料型別:可以是基本資料型別和引用資料型

c++普通變數引用變數 ,指標變數用例項讓你親自體會

int a1 = 10; int a2 = 20; int a3 = a1;//賦值語句,可以理解成資料的克隆,a3與a1 不在是指向一個物件 int &b = a1; //int &b2 = 900; //不合法,非常量引用的初始值必須左值 int *c =