java并发编程 ThreadLocal


一、使用

  1.1 不用TheadLocal会有什么问题?

    多线程访问共享变量时,由于线程不安全问题,导致num得到的结果是无法确定的。此时如果变量是私有的,就用ThreadLocal,如果是共享的就要加锁。

public class ThreadLocalTest01 {
    public static Integer num = 0;

    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                num += 5;
                System.out.println("num = " + num);
            }).start();
        }
    }
}

  1.2 使用ThreadLocal

    使得num是线程私有的,跟其他线程隔离

public class ThreadLocalTest02 {
    public static ThreadLocal num = ThreadLocal.withInitial(() -> 0);

    public static void main(String[] args) {
        Thread[] threads = new Thread[5];
        for (int i = 0; i < 5; i++) {
            threads[i] = new Thread(() -> {
                Integer localNum = num.get();
                localNum += 5;
                num.set(localNum);
                System.out.println(Thread.currentThread().getName() + ", num = " + localNum);
            }, "thread-" + i);
        }
        for (Thread thread : threads) {
            thread.start();
        }
    }
}

 

二、要搞清以下问题

2.1 每个线程的变量副本 是如何存储的?

   数组有hash冲突,没有使用链表/红黑树解决,而是用黄金分割(斐波那契散列),能均衡分配到16个槽位上,超过16就扩容

  hashmap为什么不用,因为map是用来做存储的

/**
 * @author
 * @Description 结果一定是0~15,不重复
 * @date 2022/3/2
 */
public class Demo {
    private static final int HASH_INCREMENT = 0x61c88647;

    public 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();
    }

    public static void main(String[] args) {
        magicHash(16);
    }
}

上面这张图详细的揭示了ThreadLocal和Thread以及ThreadLocalMap三者的关系。

1、Thread中有一个map,就是ThreadLocalMap

2、ThreadLocalMap的key是ThreadLocal,值是我们自己设定的。

3、ThreadLocal是一个弱引用,当为null时,会被当成垃圾回收

4、重点来了,突然我们ThreadLocal是null了,也就是要被垃圾回收器回收了,但是此时我们的ThreadLocalMap生命周期和Thread的一样,它不会回收,这时候就出现了一个现象。那就是ThreadLocalMap的key没了,但是value还在,这就造成了内存泄漏。

解决办法:使用完ThreadLocal后,执行remove操作,避免出现内存溢出情况。

相关