zoukankan      html  css  js  c++  java
  • Java ThreadLocal 该类提供了线程局部 (thread-local) 变量

      ThreadLocal,可以理解为线程的局部变量,作用就是为每一个使用该变量的线程都提供一个变量值的副本,每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。

         ThreadLocal是如何做到为每一个线程维护变量的副本的呢?

    每个线程中都有一个ThreadLocalMap(Thread.threadLocals),用于存储每一个线程的变量的副本。

    ThreadLocalMap使用数组Entry[] table保存ThreadLocal-->Object键值对象,数组保存位置:int i = key.nextHashCode() & (table.length - 1);

      ThreadLocal和Synchonized区别:

      都用于解决多线程并发访问。
      Synchronized用于线程间的数据共享(使变量或代码块在某一时该只能被一个线程访问),是一种以延长访问时间来换取线程安全性的策略;
      而ThreadLocal则用于线程间的数据隔离(为每一个线程都提供了变量的副本),是一种以空间来换取线程安全性的策略。
     
      源码中有以下一段

    ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {
    table = new Entry[INITIAL_CAPACITY];
    int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
    table[i] = new Entry(firstKey, firstValue);
    size = 1;
    setThreshold(INITIAL_CAPACITY);
    }

    ThreadLocalMap 中 Entry[] table 的大小必须是2的N次方(INITIAL_CAPACITY = 2^N),那INITIAL_CAPACITY - 1的二进制表示就是低位连续的N个1, 那 firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);的值就是 threadLocalHashCode 的低N位。

    测试:

    private static AtomicInteger nextHashCode = new AtomicInteger();

    private static final int HASH_INCREMENT = 0x61c88647;

    private static int nextHashCode() {
    return nextHashCode.getAndAdd(HASH_INCREMENT);
    }

    public static void main(String[] args) {
    for (int j = 0; j < 5; j++) {
    int size = 2 << j;
    // hash = 0;
    int[] indexArray = new int[size];
    for (int i = 0; i < size; i++) {
    indexArray[i] = nextHashCode() & (size - 1);
    }
    System.out.println("indexs = "+ Arrays.toString(indexArray));
    }
    }

    结果:

    indexs = [0, 1]
    indexs = [2, 1, 0, 3]
    indexs = [2, 1, 0, 7, 6, 5, 4, 3]
    indexs = [2, 9, 0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11]
    indexs = [18, 25, 0, 7, 14, 21, 28, 3, 10, 17, 24, 31, 6, 13, 20, 27, 2, 9, 16, 23, 30, 5, 12, 19, 26, 1, 8, 15, 22, 29, 4, 11]

    没有看到重复的索引值,要哈希表的大小是2的N次方,那么基本上可以保证每次计算出的index值都不会重复。

    为什么HashCode不直接用自增的方式(HASH_INCREMENT=1)?

    我的理解是,随着不用的 ThreadLocal 变量被回收掉,这种自增的方式的性能会越来越差,因为临近的 slot 为空的可能性很小。而 ThreadLocal 实际所采用的方式,其下标是在跳跃分布,这样即使出现冲突,在临近找到空 slot 的可能性更大一些,性能也会更好。

    维护每个线程的ThreadId:

    public class ThreadId {
    // Atomic integer containing the next thread ID to be assigned
    private static final AtomicInteger nextId = new AtomicInteger(0);

    // Thread local variable containing each thread's ID
    private static final ThreadLocal<Integer> threadId = new ThreadLocal<Integer>(){
    @Override
    protected Integer initialValue () {
    return nextId.getAndIncrement();
    }
    };

    // Returns the current thread's unique ID, assigning it if necessary
    public static int get() {
    return threadId.get();
    }
    }

  • 相关阅读:
    字符串常量
    二维数组中的查找
    Codeforces 156B Suspects——————【逻辑判断】
    Codeforces 156 A——Message——————【思维题】
    Codeforces 639B——Bear and Forgotten Tree 3——————【构造、树】
    Codeforces 671 A——Recycling Bottles——————【思维题】
    Codeforces 550D —— Regular Bridge——————【构造】
    Codeforces 550C —— Divisibility by Eight——————【枚举 || dp】
    codeforces 638B—— Making Genome in Berland——————【类似拓扑排序】
    codeforces 675 C ——Money Transfers——————【思维题】
  • 原文地址:https://www.cnblogs.com/shann/p/7107577.html
Copyright © 2011-2022 走看看