• ThreadLocal的魔数为什么是0x61c88647?


    ThreadLocal的魔数为什么是0x61c88647?

    原创漠狐烟 发布于2019-12-15 12:53:31 阅读数 17  收藏

    展开

    我们通过上一篇文章分析得知ThreadLocal用map就避免不了冲突的产生,为了解决散列表的冲突而引入的神奇的hash code: 0x61c88647,可以让生成出来的值或者说ThreadLocal的ID较为均匀地分布在2的幂大小的数组中。
    碰撞避免和解决

    1. 只有一个ThreadLocal实例的时候,官方推荐的做法,声明为public static,当向threadLocal变量中设置多个值的时产生的碰撞,碰撞解决是通过开放定址法, 且是线性探测(linear-probe)

    2. 有多个ThreadLocal实例的时候,最极端的是每个线程都new一个ThreadLocal实例,此时利用特殊的哈希码0x61c88647大大降低碰撞的几率, 同时利用开放定址法处理碰撞

    看ThreadLocal源码中这个哈希码出现的地方:

    /**
     * ThreadLocals rely on per-thread linear-probe hash maps attached
     * to each thread (Thread.threadLocals and inheritableThreadLocals).
     * The ThreadLocal objects act as keys, searched via threadLocalHashCode.
     * This is a custom hash code (useful only within ThreadLocalMaps) that
     * eliminates collisions in the common case where consecutively
     * constructed ThreadLocals are used by the same threads,
     * while remaining well-behaved in less common cases.
     */
    private final int threadLocalHashCode = nextHashCode();
    
    /**
     * The next hash code to be given out. Updated atomically.
     * Starts at zero.
     */
    private static AtomicInteger nextHashCode = new AtomicInteger();
    
    /**
     * The difference between successively generated hash codes - turns
     * implicit sequential thread-local IDs into near-optimally spread
     * multiplicative hash values for power-of-two-sized tables.
     */
    private static final int HASH_INCREMENT = 0x61c88647;
    
    /**
     * Returns the next hash code.
     */
    private static int nextHashCode() {
        return nextHashCode.getAndAdd(HASH_INCREMENT); 
    }
    /**
     * The table, resized as necessary.
     * table.length MUST always be a power of two.
     */
     //table的长度必须是2的N次方
    private Entry[] table;
    

    每当创建ThreadLocal实例时这个值都会累加 0x61c88647, 目的在上面的注释中已经写的很清楚了:为了让哈希码能均匀的分布在2的N次方的数组里.ThreadLocalMap 中 Entry[] table 的大小必须是2的N次方(len = 2^N),那 len-1 的二进制表示就是低位连续的N个1也就是 threadLocalHashCode 的低N位 这样就能均匀的产生均匀的分布.写段程序验证下:

        private static final int HASH_INCREMENT = 0x61c88647;
        public static void main(String[] args) {
            magicHash(16); //初始大小16
            magicHash(32); //扩容一倍
        }
    
        private static void magicHash(int size){
            int hashCode = 0;
            for(int i=0;i<size;i++){
                hashCode = i*HASH_INCREMENT+HASH_INCREMENT;
                System.out.print((hashCode & (size-1))+" ");
            }
            System.out.println();
        }
    

    控制台打印结果:
    result

    拓展
    这个魔数的选取与斐波那契散列有关,0x61c88647对应的十进制为1640531527。
    斐波那契散列的乘数可以用(long) ((1L << 31) * (Math.sqrt(5) - 1))可以得到2654435769,如果把这个值给转为带符号的int,则会得到-1640531527。
    换句话说
    (1L << 32) - (long) ((1L << 31) * (Math.sqrt(5) - 1))得到的结果就是1640531527也就是0x61c88647

    long l = (long) ((1L << 31) * (Math.sqrt(5) - 1));
    System.out.println("32位无符号整数: " + l);
    int i = (int) l;
    System.out.println("32位有符号整数:   " + i);
    

    result

  • 相关阅读:
    [转]Java 反射在实际开发中的应用
    phantomjs生成网页快照,有些https 证书不支持问题解决
    PHP + TrackingMore物流信息跟踪
    rabbitMQ消息队列 – Message方法解析
    MySQL UNION 与 UNION ALL 语法与用法
    提高mysql千万级大数据SQL查询优化30条经验(Mysql索引优化注意)
    php商品对比功能代码分享
    Redis类的源码使用
    php+redis实现消息队列
    PHP中利用redis实现消息队列处理高并发请求
  • 原文地址:https://www.cnblogs.com/grj001/p/12223146.html
Copyright © 2020-2023  润新知