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 ThreadLocalnum = 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操作,避免出现内存溢出情况。