• Java中Integer和ThreadLocal


    一. 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的。

  • 相关阅读:
    DedeCMS图集上传图片报错,FILEID:X 错误处理办法
    AE10.0打开MxD或shp文件时提示“The specified path is invalid”
    未能加载AE的ESRI.ArcGIS.3Danalyst.dll等程序集
    Intel Parallel Studio 2011: error2350 FDI server error
    Android Google Map APIKey申请
    关于程序堆栈的解释
    Major and Minor Numbers (主次设备号)这个听说过
    linux字符设备驱动之LED
    linux下ioctl函数学习
    Some Important Data Structures
  • 原文地址:https://www.cnblogs.com/lxyit/p/9024191.html
Copyright © 2020-2023  润新知