博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java并发编程:线程封闭和ThreadLocal详解
阅读量:5956 次
发布时间:2019-06-19

本文共 4195 字,大约阅读时间需要 13 分钟。

转载请标明出处:

本文出自

什么是线程封闭

当访问共享变量时,往往需要加锁来保证数据同步。一种避免使用同步的方式就是不共享数据。如果仅在单线程中访问数据,就不需要同步了。这种技术称为线程封闭。在Java语言中,提供了一些类库和机制来维护线程的封闭性,例如局部变量和ThreadLocal类,本文主要深入讲解如何使用ThreadLocal类来保证线程封闭。

理解ThreadLocal类

ThreadLocal类能使线程中的某个值与保存值的对象关联起来,它提供了get、set方法,这些方法为每个使用该变量的线程保存一份独立的副本,因此get总是set当前线程的set最新值。

首先我们来看个例子,这个例子来自于

public class Test1 {    ThreadLocal
longLocal = new ThreadLocal
(); ThreadLocal
stringLocal = new ThreadLocal
(); public void set() { longLocal.set(Thread.currentThread().getId()); stringLocal.set(Thread.currentThread().getName()); } public long getLong() { return longLocal.get(); } public String getString() { return stringLocal.get(); } public static void main(String[] args) throws InterruptedException { final Test1 test = new Test1(); test.set(); System.out.println(test.getLong()); System.out.println(test.getString()); Thread thread1 = new Thread(() -> { test.set(); System.out.println(test.getLong()); System.out.println(test.getString()); }); thread1.start(); thread1.join(); System.out.println(test.getLong()); System.out.println(test.getString()); }}复制代码

运行该程序,代码输出的结果为:

1

main

10

Thread-0

1

main

从这段代码可以看出在mian线程和thread1线程确实都保存着各自的副本,它们的副本各自不干扰。

ThreadLocal源码解析

来从源码的角度来解析ThreadLocal这个类,这个类存放在java.lang包,这个类有很多方法。

image.png

它内部又个ThreadLocalMap类,主要有set()、get()、setInitialValue 等方法。

首先来看下set方法,获取当前Thread的 map,如果不存在则新建一个并设置值,如果存在设置值,源码如下:

public void set(T value) {        Thread t = Thread.currentThread();        ThreadLocalMap map = getMap(t);        if (map != null)            map.set(this, value);        else            createMap(t, value);    }复制代码

跟踪createMap,可以发现它根据Thread创建来一个ThreadLocalMap。

void createMap(Thread t, T firstValue) {        t.threadLocals = new ThreadLocalMap(this, firstValue);    }复制代码

t.threadLocals为当前线程的一个变量,也就是ThreadLocal的数据都是存放在当前线程的threadLocals变量里面的,由此可见用ThreadLocal存放的数据是线程安全的。因为它对于不同的线程来,使用ThreadLocal的set方法都会根据线程判断该线程是否存在它的threadLocals成员变量,如果没有就建一个,有的话就存下数据。

ThreadLocal.ThreadLocalMap threadLocals = null;复制代码

ThreadLocalMap为ThreadLocal的一个内部类,源码如下:

static class ThreadLocalMap {        static class Entry extends WeakReference
> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal
k, Object v) { super(k); value = v; } }复制代码

可以看到ThreadLocalMap的Entry继承了WeakReference,并且使用ThreadLocal作为键值。

在使用ThreadLocal的get方法之前一定要先set,要不然会报空指针异常。还有一种方式就是在初始化的时候调用initialValue()方法赋值。改造下之前的例子,代码如下:

public class Test2 {    ThreadLocal
longLocal = new ThreadLocal
(){ @Override protected Long initialValue() { return Thread.currentThread().getId(); } }; ThreadLocal
stringLocal = new ThreadLocal
(){ @Override protected String initialValue() { return Thread.currentThread().getName(); } }; public long getLong() { return longLocal.get(); } public String getString() { return stringLocal.get(); } public static void main(String[] args) throws InterruptedException { final Test2 test = new Test2(); System.out.println(test.getLong()); System.out.println(test.getString()); Thread thread1 = new Thread(() -> { System.out.println(test.getLong()); System.out.println(test.getString()); }); thread1.start(); thread1.join(); System.out.println(test.getLong()); System.out.println(test.getString()); }}复制代码

运行该程序,代码输出的结果为:

1

main

10

Thread-0

1

main

ThreadLocal常用的使用场景

通常讲JDBC连接保存在ThreadLocal对象中,每个对象都有属于自己的连接,代码如下:

private static ThreadLocal
connectionHolder= new ThreadLocal
() { public Connection initialValue() { return DriverManager.getConnection(DB_URL); }};public static Connection getConnection() { return connectionHolder.get();}复制代码

参考资料

《Java并发编程实战》

《深入理解JVM》

关注我的公众号

精彩内容不能错过!

forezp.jpg
你可能感兴趣的文章
切图崽的自我修养-[ES6] 编程风格规范
查看>>
服务器迁移小记
查看>>
FastDFS存储服务器部署
查看>>
Android — 创建和修改 Fragment 的方法及相关注意事项
查看>>
流程控制: jQ Deferred 与 ES6 Promise 使用新手向入坑!
查看>>
swift基础之_swift调用OC/OC调用swift
查看>>
Devexpress 15.1.8 Breaking Changes
查看>>
推荐JS插件:imagesLoaded,监测图片加载情况并提供相应的事件(加载成功/失败)...
查看>>
Java B2B2C多用户商城 springcloud架构- common-service 项目构建过程(七)
查看>>
杨老师课堂之ArrayList集合常用方法解析
查看>>
ElasticSearch Client详解
查看>>
新零售讲堂之时代下的传统零售业,何去何从?
查看>>
c++读取和写入TXT文件的整理
查看>>
深入动态人脸识别小场景应用,2019年或将迎来爆发期
查看>>
Ionic2 下处理 Android 设备下返回按钮的事件
查看>>
linux安全问答(1)
查看>>
zabbix监控进程的CPU和内存占用量
查看>>
Error creating bean with name 'userServiceImpl': Injection of autowired dependencies failed
查看>>
mybatis update返回值的意义
查看>>
SVNQuery–如何创建更新索引并查询
查看>>