threadlocal为什么是弱引用,threadlocal为什么是安全的

首页 > 技术 > 作者:YD1662023-04-15 23:32:30

前言

大家好,我是捡田螺的小男孩

无论是工作还是面试,我们都会跟ThreadLocal打交道,今天就跟大家聊聊ThreadLocal哈~

  1. ThreadLocal是什么?为什么要使用ThreadLocal
  2. 一个ThreadLocal的使用案例
  3. ThreadLocal的原理
  4. 为什么不直接用线程id作为ThreadLocalMap的key
  5. 为什么会导致内存泄漏呢?是因为弱引用吗?
  6. Key为什么要设计成弱引用呢?强引用不行?
  7. InheritableThreadLocal保证父子线程间的共享数据
  8. ThreadLocal的应用场景和使用注意点
1. ThreadLocal是什么?为什么要使用ThreadLocal?

ThreadLocal是什么?

ThreadLocal,即线程本地变量。如果你创建了一个ThreadLocal变量,那么访问这个变量的每个线程都会有这个变量的一个本地拷贝,多个线程操作这个变量的时候,实际是在操作自己本地内存里面的变量,从而起到线程隔离的作用,避免了并发场景下的线程安全问题。

//创建一个ThreadLocal变量 static ThreadLocal<String> localVariable = new ThreadLocal<>(); 复制代码

为什么要使用ThreadLocal

并发场景下,会存在多个线程同时修改一个共享变量的场景。这就可能会出现线性安全问题

为了解决线性安全问题,可以用加锁的方式,比如使用synchronized 或者Lock。但是加锁的方式,可能会导致系统变慢。加锁示意图如下:

threadlocal为什么是弱引用,threadlocal为什么是安全的(1)

还有另外一种方案,就是使用空间换时间的方式,即使用ThreadLocal。使用threadLocal类访问共享变量时,会在每个线程的本地,都保存一份共享变量的拷贝副本。多线程对共享变量修改时,实际上操作的是这个变量副本,从而保证线性安全。

threadlocal为什么是弱引用,threadlocal为什么是安全的(2)

2. 一个threadLocal的使用案例

日常开发中,ThreadLocal经常在日期转换工具类中出现,我们先来看个反例

/** * 日期工具类 */ public class DateUtil { private static final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); public static Date parse(String dateString) { Date date = null; try { date = simpleDateFormat.parse(dateString); } catch (ParseException e) { e.printStackTrace(); } return date; } } 复制代码

我们在多线程环境跑DateUtil这个工具类:

public static void main(String[] args) { ExecutorService executorService = Executors.newFixedThreadPool(10); for (int i = 0; i < 10; i ) { executorService.execute(()->{ System.out.println(DateUtil.parse("2022-07-24 16:34:30")); }); } executorService.shutdown(); } 复制代码

运行后,发现报错了:

threadlocal为什么是弱引用,threadlocal为什么是安全的(3)

如果在DateUtil工具类,加上ThreadLocal,运行则不会有这个问题:

/** * 日期工具类 */ public class DateUtil { private static ThreadLocal<SimpleDateFormat> dateFormatThreadLocal = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")); public static Date parse(String dateString) { Date date = null; try { date = dateFormatThreadLocal.get().parse(dateString); } catch (ParseException e) { e.printStackTrace(); } return date; } public static void main(String[] args) { ExecutorService executorService = Executors.newFixedThreadPool(10); for (int i = 0; i < 10; i ) { executorService.execute(()->{ System.out.println(DateUtil.parse("2022-07-24 16:34:30")); }); } executorService.shutdown(); } } 复制代码

运行结果:

Sun Jul 24 16:34:30 GMT 08:00 2022 Sun Jul 24 16:34:30 GMT 08:00 2022 Sun Jul 24 16:34:30 GMT 08:00 2022 Sun Jul 24 16:34:30 GMT 08:00 2022 Sun Jul 24 16:34:30 GMT 08:00 2022 Sun Jul 24 16:34:30 GMT 08:00 2022 Sun Jul 24 16:34:30 GMT 08:00 2022 Sun Jul 24 16:34:30 GMT 08:00 2022 Sun Jul 24 16:34:30 GMT 08:00 2022 Sun Jul 24 16:34:30 GMT 08:00 2022 复制代码

刚刚反例中,为什么会报错呢?这是因为SimpleDateFormat不是线性安全的,它以共享变量出现时,并发多线程场景下即会报错。

为什么加了ThreadLocal就不会有问题呢?并发场景下,ThreadLocal是如何保证的呢?我们接下来看看ThreadLocal的核心原理。

3. ThreadLocal的原理3.1 ThreadLocal的内存结构图

为了有个宏观的认识,我们先来看下ThreadLocal的内存结构图

threadlocal为什么是弱引用,threadlocal为什么是安全的(4)

首页 123下一页

栏目热文

文档排行

本站推荐

Copyright © 2018 - 2021 www.yd166.com., All Rights Reserved.