1. 程式人生 > >Java中Integer和ThreadLocal

Java中Integer和ThreadLocal

PV als 基於 本質 log lang static 返回 類構造

一. Integer

1.引子

在開始之前,我還是需要吐槽下自己,我是真的很菜!

  • 他問:**兩個Integer對象==比較是否相等?
  • 我答:對象==比較,是引用比較,不相等!
  • 他問:IntegerCache這個用來幹什麽?
  • 我答:......,我不是很清楚!!!

從這裏可以看出,我是真的很水!!
基於這些原因還有其他的等等,我開始寫博文,開始寫踩過的坑!

2.IntegerCache

在介紹這個緩存之前,先來認知下Integer。java中,int是基本的數值類型,表示整型;Integer是其對象的表示,是int的包裝類。包裝類在Jdk1中就存在,但是為了更方便的使用Integer,在Java SE5中引入自動包裝和自動拆箱的機制,這裏不再贅述,可以移步至深入剖析Java中的裝箱和拆箱。
Java SE5可以說是java版本叠代過程中裏程碑版本。為了減少大量創建Integer的開銷、提高性能,采用享元模式,引入Integer實例緩存,將-128至127範圍數值的Integer實例緩存在內存中

,這裏的緩存就是使用Integer中的輔助性的私有IntegerCache靜態類實現。

先看下如何獲取緩存的-128至127範圍的Integer實例,這裏的範圍可以通過JVM啟動參數修改,後面會講述

  • 可以通過使用靜態valueOf方法獲取 // api調用
  • 可以使用自動包裝機制獲取,如:Integer a = 100; // autoboxing

這裏基於Java SE8,再來看下IntegerCache的實現:

/**
 * Cache to support the object identity semantics of autoboxing for values between
 * -128 and 127 (inclusive) as required by JLS.
 *
 * The cache is initialized on first usage.  The size of the cache
 * may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option.
 * During VM initialization, java.lang.Integer.IntegerCache.high property
 * may be set and saved in the private system properties in the
 * sun.misc.VM class.
 */

private static class IntegerCache {
    static final int low = -128;
    static final int high;
    static final Integer cache[];

    static {
        // high value may be configured by property
        int h = 127;
        String integerCacheHighPropValue =
            sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
        if (integerCacheHighPropValue != null) {
            try {
                int i = parseInt(integerCacheHighPropValue);
                i = Math.max(i, 127);
                // Maximum array size is Integer.MAX_VALUE
                h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
            } catch( NumberFormatException nfe) {
                // If the property cannot be parsed into an int, ignore it.
            }
        }
        high = h;

        cache = new Integer[(high - low) + 1];
        int j = low;
        for(int k = 0; k < cache.length; k++)
            cache[k] = new Integer(j++);

        // range [-128, 127] must be interned (JLS7 5.1.7)
        assert IntegerCache.high >= 127;
    }

    private IntegerCache() {}
}

從java docs中可以看出,該類是為了支持對-128至127範圍數值自動包裝的實例進行緩存。
在第一次使用時緩存被初始化,緩存的大小可以通過JVM參數-XX:AutoBoxCacheMax=size控制,IntegerCache的high可以通過在JVM啟動時設置系統屬性變量java.lang.Integer.IntegerCache.high,以達到更強的靈活性。IntegerCache在執行類構造器初始化時,執行靜態語句塊,利用for循環從low開始不斷自加加,然後放入cache數組中。
選擇-128至127範圍,是因為這個範圍內的數值使用更普遍!

介紹到這裏,相信已經不難看出我的回答是多麽搞笑,我坐井觀天的還以為自己是正確的!
說到這裏,我又有些感慨了,昨晚看了集火影,剛好是自來也和佩恩的對話,佩恩對曾經的師傅自來也的一席話,雖然是偏執的,但是無可否認的確是對的。
佩恩把自己稱為神,將自來也視作為凡人,說凡人的認知具有局限性,活在狹小的空間內自認為自己認識都是對的,殊不知從神的角度考慮,凡人的那些認知都是錯誤和愚蠢的。
這裏旨在單純思考佩恩這些話在我們現實生活中的含義:的確,在我們認知有限的時候,以為Integer兩個對象==比較,應該是不等的,殊不知因為技術掌握有限。

跑題了,引入一段故事,主要是想銘刻下,吸取教訓:不要把事物看做是理所當然,要多多思考其本質和原理;不要妄下結論,小心求證!

不僅是Integer,Short、Byte、Long、Char都具有相應的Cache。但是Byte、Short、Long的Cache有固定範圍:-128至127;Char的Cache:0至127。

引用
理解Java Integer的緩存策略

二. ThreadLocal

我這裏不再贅述ThreadLocal是什麽、解決什麽問題、實現原理、應用場景等,網上博文非常豐富,可移步至深入剖析ThreadLocal。
這裏主要是分析下ThreadLocal在父子線程中共享的問題。
ThreadLocal不提供對子線程共享父線程的ThreadLocal中存儲的value的支持,ThreadLocal的get方法返回與當前線程有關的ThreadLocalMap中的key/value,如下圖:

/* ThreadLocal values pertaining to this thread. This map is maintained
 * by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;

每個線程在創建的時候,都會初始化ThreadLocalMap的空引用。 子線程在創建時ThreadLocaMap是null,所以不共享父線程的ThreadLocalMap中key/value。

但是有很多時候,實際需求需要共享父線程的一些狀態值。 這種情況,java也對此提供了支持,只能說設計者們的腦洞大開。
java中引入另一個InheritableThreadLocal類對此提供支持,類名給出了用途的最好表示:可繼承的ThreadLocal

/* ThreadLocal values pertaining to this thread. This map is maintained
 * by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;

同理,每個線程在創建的時候都會初始化InheritableThreadLocal
的引用,然後在線程執行初始化的生命周期階段時,對該變量進行初始化操作,如下圖:

    if (parent.inheritableThreadLocals != null)
        this.inheritableThreadLocals =
            ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);

線程的初始化周期中,首先判斷父線程的ThreadLocal是否為空,如果不為空則根據父線程的inheritableThreadLocals創建子線程的inheritableThreadLocals。
InheritableThreadLocal的實現也非常簡單:

  • childValue: 計算子線程的初始化值,從這個方法中可以知道,子線程使用的是父線程中的value,並不是拷貝,如果需要使用副本拷貝,可以繼承重寫
  • getMap: 返回當前線程持有的inheritableThreadLocals
  • createMap: 如果當前線程的inheritableThreadLocals是空,則創建一個

以上三個api都是重寫ThreadLocal的。

Java中Integer和ThreadLocal