ThreadLocal实现导图及其原理解析
来源:互联网 发布:网络最近流行的舞蹈 编辑:程序博客网 时间:2024/05/21 18:45
首先我们来看生活中的一个情景:我们到银行办卡前必须填表,如果人多而笔只有一支,那么填表需要花费大量的时间。如果是人手一支笔的话,那就能省去排队等笔的时间,从而大大减少了填表时间。在并发编程中,ThreadLocal就解决了上述问题。
1. 资源竞争带来的麻烦
我们以一段ThreadLocal的应用代码为契机,来看看ThreadLocal是如何实现“人手一支”,从而减小资源竞争的。
我们先来看一个没用ThreadLocal的demo
public class WithoutThreadLocalDemo { public static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //线程共享变量 public static class ParseDate implements Runnable{ int i = 0; public ParseDate(int i) { this.i = i; } @Override public void run() { try { Date date = simpleDateFormat.parse("2017-09-06 19:29:" + i%60); //使用共享变量 System.out.println(i + ":" + date + System.currentTimeMillis()); } catch (ParseException e) { e.printStackTrace(); } } } public static void main(String[] args) throws InterruptedException { ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(10); for (int i = 0; i < 10; i++){ //开启10个线程 newFixedThreadPool.execute(new ParseDate(i)); } newFixedThreadPool.shutdown(); }}
如图所见,运行后程序抛出NumberFormatException,根据异常来看程序的27行
Date date = simpleDateFormat.parse("2017-09-06 19:29:" + i%60);
因为SimpleDateFormat是线程不安全的,当多个线程使用simpleDateFormat时产生了不符合格式的字符串,如下导图所示。但如果你足够幸运,线程间不发生中断性的竞争就不会出现上述情况。
2. 使用ThreadLocal 解决竞争
接下来我们使用ThreadLocal解决上述问题
public class ThreadLocalDemo { public static ThreadLocal<SimpleDateFormat> threadLocal = new ThreadLocal<SimpleDateFormat>(); //创建threadLocal变量 // public static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); public static class ParseDate implements Runnable{ int i = 0; public ParseDate(int i) { this.i = i; } @Override public void run() { try { if (threadLocal.get() == null){ threadLocal.set(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")); //向threadLocal中设值 } Date date = threadLocal.get().parse("2017-09-06 19:29:" + i%60); //从threadLocal中取值使用 System.out.println(i + ":" + date + System.currentTimeMillis()); } catch (ParseException e) { e.printStackTrace(); } finally { threadLocal.remove(); } } } public static void main(String[] args) throws InterruptedException { ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(10); for (int i = 0; i < 10; i++){ //创建10个线程 newFixedThreadPool.execute(new ParseDate(i)); } newFixedThreadPool.shutdown(); }}
运行结果:
2.1 从ThreadLocal源码入手
从程序中可以发现,ThreadLocal的get()和set()方法在整个程序中起了重要作用,先来看set()的源码。
通过源码可知每个线程自带ThreadLocalMap属性,执行set()方法时以ThreadLocal作为键,变量作为值存入ThreadLocalMap。
get()方法先获得当前线程,以ThreadLocal为键从线程的ThreadLocalMap中获取到相应的值。
2.2 关系导图
这就好比,每个排队的人自带了一个袋子,ThreadLocal是凭袋子发笔的机器,自动向每个排队人的口袋里放笔。
在这里值得注意的一点是:ThreadLocalMap的key对ThreadLocal的引用是弱引用。(详细请看《实战Java高并发程序设计》4.3.2)
3. ThreadLocal的资源回收问题
红框部分的代码用finally进行了包裹,大家都知道用finally包裹的代码是一定会执行的,为什么要这么做呢?
在demo中,我们用到了线程池。执行完后,线程池中的线程依旧存在,线程ThreadLocalMap属性内存放的变量不会被回收。如果ThreadLocalMap中存放很大的变量而一直不回收,很有可能导致内存泄露。因此手动回收变量显得很有必要,remove()会将线程ThreadLocalMap中的变量移除。
本文部分代码参考《实战Java高并发程序设计》,同时也向大家推荐下这本书。有不足或错误的地方,希望大家及时向作者反馈,欢迎大家的吐槽,QQ375035834!
- ThreadLocal实现导图及其原理解析
- Java Thread&Concurrency(10): 深入理解ThreadLocal及其实现原理
- ThreadLocal原理解析
- ThreadLocal---原理解析
- ThreadLocal原理解析
- ThreadLocal原理解析
- ThreadLocal原理解析
- ThreadLocal原理及其实际应用
- ThreadLocal原理及其实际应用
- ThreadLocal原理及其实际应用
- ThreadLocal原理及其实际应用
- ThreadLocal代码实现原理
- ThreadLocal的实现原理
- ThreadLocal实现原理
- threadLocal内部实现原理
- ThreadLocal的实现原理
- spring AOP 源码解析 及其实现原理
- ThreadLocal使用及原理解析
- BZOJ[1385][Baltic2000]Division expression 乱搞
- Kotlin 学习之包和导入
- 统计学习方法笔记(二)感知机
- Learning Rate Annealing
- HDU1358 Period
- ThreadLocal实现导图及其原理解析
- logstash
- http上enctype属性等字符编码浅谈
- 文章标题
- php文件的上传和下载
- 垃圾回收的算法
- 校园导游咨询——数据结构课程设计
- HIVE学习笔记:HiveServer2Beeline
- mysql批量查询