• 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<Integer> 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);
        }
    }
    2.2 是ThreadLocal怎么初始化的

    2.3 使用场景那也是相当的丰富:
      1、在进行对象跨层传递的时候,使用ThreadLocal可以避免多次传递,打破层次间的约束。
      2、线程间数据隔离
      3、进行事务操作,用于存储线程事务信息。
      4、数据库连接,Session会话管理。
    作用:
      因为每个 Thread 内有自己的实例副本,且该副本只能由当前 Thread 使用。这是也是 ThreadLocal 命名的由来。
      既然每个 Thread 有自己的实例副本,且其它 Thread 不可访问,那就不存在多线程间共享的问题

    四、ThreadLocal其他几个注意的点

    只要是介绍ThreadLocal的文章都会帮大家认识一个点,那就是内存泄漏问题。我们先来看下面这张图。

    /**
     * -Xms20M -Xmx20M -Xmn10M
     */
    public class ThreadLocalOOM {
        public static void main(String[] args) {
            System.out.println("main 线程开始...");
            for (int i = 0; i < 100; i++) {
                ThreadLocal<Byte[]> threadLocal = new ThreadLocal<>();
                //  在当前线程存入一个value
                System.out.println("在线程:"+Thread.currentThread().getName()+" 添加一个threadLocal");
                threadLocal.set(new Byte[1024*1024]);
                // 方法解决,将threadLocal remove
                threadLocal.remove();
            }
            System.out.println("main 线程完美运行结束");
        }
    }

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

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

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

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

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

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

  • 相关阅读:
    mongodb的安装和使用
    python小笔记
    爬取猫眼TOP100电影
    python测接口
    Python数据驱动(ddt)
    Python文件读写(csv、excel)
    python爬虫入门(转:http://www.cnblogs.com/voidsky/p/5490810.html)
    python bs4的使用
    Django 中的 model 继承
    HTTP幂等性
  • 原文地址:https://www.cnblogs.com/jly594761082/p/15953589.html
Copyright © 2020-2023  润新知