浅谈 ThreadLocal
有时,你希望将每个线程数据(如用户ID)与线程关联起来。尽管可以使用局部变量来完成此任务,但只能在本地变量存在时才这样做。也可以使用一个实例属性来保存这些数据,但是这样就必须处理线程同步问题。幸运的是,Java提供了java.lang.ThreadLocal类是一个简单(而且非常方便)的选择。
每个ThreadLocal实例都描述了一个线程局部变量,它是一个变量,它为每个访问变量的线程提供一个单独的存储槽(storage slot)。你可以将线程局部变量看作是一个多槽变量,其中每个线程可以在同一个变量中存储不同的值。每个线程只看到它的值,并且不知道其他线程在这个变量中有自己的值。
一般将ThreadLocal声明为ThreadLocal <T>,其中T标识存储在该变量中的值的类型。 这个类声明了下面的构造方法和方法:
ThreadLocal():创建一个新的线程局部变量。T get():返回调用线程存储槽中的值。 如果线程调用此方法时不存在条目,则get()方法调用initialValue()方法。T initialValue():创建调用线程的存储槽并在此槽中存储初始(默认)值。 初始值默认为null。 必须子类化ThreadLocal并重写此受保护的方法以提供更合适的初始值。void remove():删除调用线程的存储槽。如果这个方法后面是get(),而没有插入set()方法,则get()调用initialValue()。
*void set(T value):将调用线程的存储槽的值设置为value。
下面代码显示了如何使用ThreadLocal将不同的用户ID与两个线程相关联的示例。
package concurrency;
public class ThreadLocalDemo {
private static volatile ThreadLocal<String> userID = new ThreadLocal<String>();
public static void main(String[] args) {
Runnable task = () -> {
String name = Thread.currentThread().getName();
if (name.equals("A")) {
userID.set("yaya");
} else {
userID.set("maomao");
}
System.out.println(name + " " + userID.get());
};
Thread threadA = new Thread(task);
threadA.setName("A");
Thread threadB = new Thread(task);
threadB.setName("B");
threadA.start();
threadB.start();
} // end method main
} // end class ThreadLocalDemo
在实例化ThreadLocal并将引用分配给名为userID的volatile实例属性(该属性是volatile的,因为它是由不同的线程访问,这可能在多处理器/多核机器上执行——也可以指定为final),默认主线程创建两个线程在userID中存储不同的String对象并输出它们的值。
在Eclipse运行此程序,输出的结果为:
A yaya
B maomao
存储在线程局部变量中的值都互不相关的。 当一个新的线程被创建时,它会得到一个包含initialValue()值的新的存储槽。 也许你想从父线程(创建另一个线程的线程)传递值到子线程(创建的线程)。 则可以使用InheritableThreadLocal完成此任务。
InheritableThreadLocal是ThreadLocal的一个子类。 除了声明的InheritableThreadLocal()构造方法外,该类声明以下受保护的方法:
T childValue(T parentValue):在创建子线程时,计算这个可继承线程局部变量的子线程的初始值,将该值作为父线程值的一个函数。在启动子线程之前,从父线程内部调用此方法。该方法仅返回其输入变量,如果所需要的是其他行为,则应该重写此方法。
下面代码显示了如何使用InheritableThreadLocal将父线程的Integer对象传递给子线程。
public class InheritableThreadLocalDemo {
private static final InheritableThreadLocal<Integer> intVal = new InheritableThreadLocal<Integer>();
public static void main(String[] args) {
Runnable rP = () -> {
intVal.set(new Integer(10));
Runnable rC = () -> {
Thread thd = Thread.currentThread();
String name = thd.getName();
System.out.printf("%s %d%n", name, intVal.get());
};
Thread thdChild = new Thread(rC);
thdChild.setName("Child");
thdChild.start();
};
new Thread(rP).start();
}
}
实例化InheritableThreadLocal并将其分配给final的实例属性(也可以使用volatile替代)intVal后,默认主线程创建一个父线程,该线程存储包含intVal中值的10的Integer对象。 父线程创建一个子线程,它访问intVal并检索其父线程的Integer对象。
执行此程序,其运行结果如下:
Child 10
浅谈 ThreadLocal的更多相关文章
- 浅谈ThreadLocal模式
一.前言: ThreadLocal模式,严格意义上不是一种设计模式,而是java中解决多线程数据共享问题的一个方案.ThreadLocal类是java JDK中提供的一个类,用来解决线程安全问题,并不 ...
- 浅谈Java线程安全
浅谈Java线程安全 - - 2019-04-25 17:37:28 线程安全 Java中的线程安全 按照线程安全的安全程序由强至弱来排序,我们可以将Java语言中各种操作共享的数据分为以下五类 ...
- 【微信小程序项目实践总结】30分钟从陌生到熟悉 web app 、native app、hybrid app比较 30分钟ES6从陌生到熟悉 【原创】浅谈内存泄露 HTML5 五子棋 - JS/Canvas 游戏 meta 详解,html5 meta 标签日常设置 C#中回滚TransactionScope的使用方法和原理
[微信小程序项目实践总结]30分钟从陌生到熟悉 前言 我们之前对小程序做了基本学习: 1. 微信小程序开发07-列表页面怎么做 2. 微信小程序开发06-一个业务页面的完成 3. 微信小程序开发05- ...
- 浅谈flask源码之请求过程
更新时间:2018年07月26日 09:51:36 作者:Dear. 我要评论 这篇文章主要介绍了浅谈flask源码之请求过程,小编觉得挺不错的,现在分享给大家,也给大家做个参考.一起跟随 ...
- Spring5.0源码学习系列之浅谈BeanFactory创建
Spring5.0源码学习系列之浅谈BeanFactory创建过程 系列文章目录 提示:Spring源码学习专栏链接 @ 目录 系列文章目录 博客前言介绍 一.获取BeanFactory主流程 二.r ...
- Spring5.0源码学习系列之浅谈循环依赖问题
前言介绍 附录:Spring源码学习专栏 在上一章的学习中,我们对Bean的创建有了一个粗略的了解,接着本文浅谈Spring循环依赖问题,这是一个面试比较常见的问题 1.什么是循环依赖? 所谓的循环依 ...
- 浅谈 Fragment 生命周期
版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/Fragment 文中如有纰漏,欢迎大家留言指出. Fragment 是在 Android 3.0 中 ...
- 浅谈 LayoutInflater
浅谈 LayoutInflater 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/View 文中如有纰漏,欢迎大家留言指出. 在 Android 的 ...
- 浅谈Java的throw与throws
转载:http://blog.csdn.net/luoweifu/article/details/10721543 我进行了一些加工,不是本人原创但比原博主要更完善~ 浅谈Java异常 以前虽然知道一 ...
随机推荐
- 【NOIP2012】疫情控制(二分,倍增,贪心)
洛谷上的题目链接,题目不在赘述 题解 既然要时间最短,首先考虑二分. 因此,考虑二分时间,问题转换为如何检查能否到达. 如果一支军队一直向上走,能够到达根节点,那么他可以通过根节点到达其他的节点,因此 ...
- 应用canvas绘制动态时钟--每秒自动动态更新时间
使用canvas绘制时钟 下文是部分代码,完整代码参照:https://github.com/lemoncool/canvas-clock,可直接下载. 首先看一下效果图:每隔一秒会动态更新时间 一. ...
- kubernetes dashboard backend源码剖析
dashboard架构主要由一个API handler 和 五个manager构成: API handler用来处理来自客户的http请求,不同的path路由到不同的的handler处理,使用的是go ...
- Entity Framework Core 之数据库迁移
前言 最近打算用.NET Core写一份开源的简易CMS系统,来练练手 所以又去深入研究了一下Entity Framework Core 发现其实有些细节园子里还是很少讲到. 特意整理了几个细节. 正 ...
- Android开发——签名包的生成
前言: 最近想要在酷安网上传apk,注册开发者的时候需要申请验证,验证需要两个apk,一个是自己的apk(需要签名),另外一个则是下载酷安的模板生成的一个签名包(使用的签名要与之前的签名自己的apk一 ...
- 设计模式——原型模式(C++实现)
#include <iostream> #include <string> using namespace std; class CPrototype { public: CP ...
- python解决图的最短路径问题
在hihoCoder上遇到一个算法题目,描述如下: 对图结构有了解的不难发现,这是经典的求图的最短路径问题.以下是python代码: def findMin(row): minL = max(row) ...
- const 相关知识 const和指针、const和引用
以前老是对const概念不清不楚,今天算是好好做个笔记总结一下.以下内容包括1)常量指针(指针本身是常量),2)指针常量(指针指向的是常量对象),3)常量引用,4)const成员函数. 常量指针,指针 ...
- Acer Aspire E1 471G 加装SSD+机械盘后无法启动的问题
老笔记本 Acer Aspire E1 471G 加装了一块 SSD 作为系统盘(win10),原机械盘格式化后,装在光驱托架上作为数据盘. 可能会出现: 系统无法启动,显示找不到启动设备,并且在F2 ...
- delete与delete[]的区别
一直对C++中的delete和delete[]的区别不甚了解,今天遇到了,上网查了一下,得出了结论.做个备份,以免丢失. C++告诉我们在回收用 new 分配的单个对象的内存空间的时候用 delete ...