并发库应用之一 & ThreadLocal实现线程范围的共享变量
ThreadLocal用于实现线程内的数据共享,即对于相同的程序代码,多个模块在同一个线程中运行时要共享一份数据,而在另外线程中运行时又共享另外一份数据。
每个线程调用全局ThreadLocal对象的set方法,就相当于往其内部的map中增加一条记录,key分别是各自的线程,value是各自的set方法传进去的值,在线程结束时可以调用ThreadLocal.clear()方法,这样会更快释放内存,不调用也可以,因为线程结束后也可以自动释放相关的ThreadLocal变量。
ThreadLocal的应用场景:
1. 订单处理包含一系列操作:减少存库量、增加一条流水台账、修改总账,这几个操作要在同一个事务中完成,通常也即同一个线程中进行处理,如果累加公司应收款的操作失败了,则应该把前面的操作回滚,否则,提交所有操作,这要求这些操作使用相同的数据库连接对象,而这些操作的代码分别位于不同的模块类中。
2. 银行转账包含一些列操作:把转出账户的余额减少,把转入账户的余额增加,这两个操作要在同一个事务中完成,它们必须使用相同的数据库连接对象,转入和转出操作的代码分别是两个不同的账户对象的方法。
3. 例如Strut2的ActionContext,同一段代码被不同的线程调用运行时,该代码操作的数据是每个线程各自的状态和数据,对于不同的线程来说,getContext方法拿到的对象都不相同,对同一个线程来说,不管调用getContext方法多少次和在哪个模块中getContext方法,拿到的都是同一个。
在web应用中,一个请求就是一个线程,那么如何区分哪些参数属于哪个线程呢?比如struts中,A用户登录,B用户也登录,那么在Action中怎么区分哪个是A用户的数据,哪个是B用户的数据。这就涉及到ThreadLocal类了,将变量与当前线程绑定。比如struts中,有一个容器类,那么A用户将数据放在A的容器中,B用户将数据放在B的容器中,然后再将容器与线程绑定,这样的话,A请求的线程处理A容器的数据,B请求的线程处理B容器的数据,而不会混淆。
实验案例:定义一个全局共享的ThreadLocal变量,然后启动多个线程向该ThreadLocal变量中存储一个随机值,接着各个线程调用另外其他多个类的方法,这多个类的方法中读取这个ThreadLocal变量的值,就可以看到多个类在同一个线程中共享同一份数据。
要求:实现对ThreadLocal变量的封装,让外界不要直接操作ThreadLocal变量。
1. 首先编写共享容器,供外部多线程使用,类名为:ThreadShareData
/**
* 定义一个线程共享的对象
* @author liangyongxing
* @createTime 2017/02/21
*/
public class ThreadShareData {
private String name;
private int age; //防止new,生成单例类
private ThreadShareData(){}
/**
* ThreadLocal:将变量与当前线程绑定,相当于Map<Thread, value>
* 此处使用的是饱汉模式构造
*/
private static ThreadLocal<ThreadShareData> threadLocal = new ThreadLocal<>();
/**
* 返回当前线程的单例
* 此处不需要使用关键字synchronized,想想为什么?
*/
public static ThreadShareData getCurrentThreadInstance() {
ThreadShareData shareData = threadLocal.get();
if (shareData == null) {
shareData = new ThreadShareData();
threadLocal.set(shareData);
}
return shareData;
} public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
2. 模拟出两个客户端,后续在主线程中分别启动这两个客户端线程进行模拟,具体的两个类分别为:ClientA和ClientB
public class ClientA {
public void get() {
ThreadShareData shardDataA = ThreadShareData.getCurrentThreadInstance();
System.out.println(String.format("ClientA name is:%s \t data.name is:%s \t data.age is:%s",
Thread.currentThread().getName(), shardDataA.getName(), shardDataA.getAge()));
}
}
public class ClientB {
public void get() {
ThreadShareData shardDataB = ThreadShareData.getCurrentThreadInstance();
System.out.println(String.format("ClientB name is:%s \t data.name is:%s \t data.age is:%s",
Thread.currentThread().getName(), shardDataB.getName(), shardDataB.getAge()));
}
}
3. 编写对应的主线程函数也就是我们平时说的主函数
public class ThreadLocalMain {
public static void main(String[] args) {
Thread th = null;
//同时启动ClientA和ClientB两次线程,查看打印结果
for (int i = 0; i < 2; i++) {
th = new Thread(new Runnable() {
@Override
public void run() {
//生成随机数
int currentRandomInt = new Random().nextInt();
System.out.println(String.format("%s--%d", Thread.currentThread().getName(), currentRandomInt));
ThreadShareData shardData = ThreadShareData.getCurrentThreadInstance();
shardData.setName(String.format("name:%d", currentRandomInt));
shardData.setAge(currentRandomInt);
//输出两个模块的值
new ClientA().get();
new ClientB().get();
}
});
th.start();
}
}
}
输出结果如下:

可以发现,线程A和线程B在第一次同步操作对象data的时候是共享对象的,而第一次的线程A和第二次的线程A他们之间是相互独立的,这就验证了ThreadLocal可以实现不同线程内的数据共享。
总结:ThreadLocal存放的类型,针对基本类型的数据封装这种应用相对很少见,而对对象类型的数据封装才是比较常见的,即让某个类针对不同线程分别创建一个独立的实例对象,见上例。用大白话的意思就是说,一个ThreadLocal代表一个变量,故其中往里只能放一个数据,你有两个变量都要线程范围内共享,则要定义两个ThreadLocal对象。如果有一百个变量要线程共享呢?那请先定义一个对象来装这一百个变量,然后在ThreadLocal存储这一个对象。
提示:局部数据线程共享讲完之后,我们就开始进入java5之后为我们提供的最新多线程友好工具类,下一篇先讲一下我们常用到的基本类型并发环境下类的使用,具体详情请查看我的下一篇博客:并发库应用之二 & Java原子性操作类应用
并发库应用之一 & ThreadLocal实现线程范围的共享变量的更多相关文章
- ThreadLocal实现线程范围的共享变量
一.如何理解线程范围内共享数据 1.static int num=0; 2.线程1访问num变量,并设置为num=2:线程2访问num变量,并设置为num=3: 3.当线程1中对象A.B.C 在访问线 ...
- 线程:ThreadLocal实现线程范围内共享变量
在web应用中,一个请求(带有请求参数)就是一个线程,那么如何区分哪些参数属于哪个线程呢?比如struts中,A用户登录,B用户也登录,那么在Action中怎么区分哪个是A用户的数据,哪个是B用户的数 ...
- java--加强之 Java5的线程并发库
转载请申明出处:http://blog.csdn.net/xmxkf/article/details/9945499 01. 传统线程技术回顾 创建线程的两种传统方式: 1.在Thread子类覆盖的r ...
- Java复习——多线程与并发库
开启一个线程 实现一个线程的方式有两种:继承Thread类.实现Runnable接口(也存在说三种的情况,第三种是使用线程并发库中的线程池创建一个线程).这两种方法都需要重写Run方法,具体的线程逻辑 ...
- Java多线程——线程范围内共享变量和ThreadLocal
多个线程访问共享对象和数据的方式 1.如果每个线程执行的代码相同,可以使用同一个Runnable对象,这个Runnable对象中有那个共享数据,例如,买票系统就可以这么做. package java_ ...
- Java多线程——线程范围内共享变量
多个线程访问共享对象和数据的方式 1.如果每个线程执行的代码相同,可以使用同一个Runnable对象,这个Runnable对象中有那个共享数据,例如,买票系统就可以这么做. package java_ ...
- Java线程与并发库高级应用-线程范围内共享数据ThreadLocal类
1.线程范围内共享变量 1.1 前奏: 使用一个Map来实现线程范围内共享变量 public class ThreadScopeShareData { static Map<Thread, In ...
- Java线程新特征——Java并发库
一.线程池 Sun在Java5中,对Java线程的类库做了大量的扩展,其中线程池就是Java5的新特征之一,除了线程池之外,还有很多多线程相关的内容,为多线程的编程带来了极大便利.为了编写高效稳定 ...
- Java多线程与并发库高级应用-java5线程并发库
java5 中的线程并发库 主要在java.util.concurrent包中 还有 java.util.concurrent.atomic子包和java.util.concurrent.lock子包 ...
随机推荐
- Scrum立会报告+燃尽图(十二月十日总第四十一次):用户推广
此作业要求参见:https://edu.cnblogs.com/campus/nenu/2018fall/homework/2484 项目地址:https://git.coding.net/zhang ...
- 互评Beta版本——可以低头,但没必要——取件帮
基于NABCD评论作品,及改进建议 1. 根据(不限于)NABCD评论作品的选题 (1)N(Need,需求) 取件帮是一款有偿互助取件的微信小程序,很大程度上解决了学生因为距离.时间等原因无法取快递的 ...
- SpringMvc跨域支持
SpringMvc跨域支持 在controller层加上注解@CrossOrigin可以实现跨域 该注解有两个参数 1,origins : 允许可访问的域列表 2,maxAge:飞行前响应的缓存持续 ...
- Leetcode题库——28.实现strStr()
@author: ZZQ @software: PyCharm @file: strStr.py @time: 2018/11/6 20:04 要求:给定一个 haystack 字符串和一个 need ...
- 图层损坏 E/ArcGIS﹕ The map or layer has been destroyed or recycled. 资源未释放
看到论坛上有个网友和我一样的问题: The map or layer has been destroyed or recyled t Hello, I have a problem when the ...
- 读《我是IT小小鸟》有感
我是一只IT小小鸟,我与IT结缘.书中是作者对个人经历与经验在IT下的体会,却给了我们很好的借鉴. IT这门行业,不仅仅再局限于如我们高中老师教学所要求的内容.IT更加开放,可以通过GitHub.CS ...
- 关于SVM数学细节逻辑的个人理解(一) :得到最大间隔分类器的基本形式
网上,书上有很多的关于SVM的资料,但是我觉得一些细节的地方并没有讲的太清楚,下面是我对SVM的整个数学原理的推导过程,其中逻辑的推导力求每一步都是有理有据.现在整理出来和大家讨论分享. 因为目前我的 ...
- Qt之美(一):d指针/p指针详解(解释二进制兼容,以及没有D指针就会崩溃的例子。有了D指针,所使用的对象大小永远不会改变,它就是该指针的大小。这个指针就被称作D指针)good
Translated by mznewfacer 2011.11.16 首先,看了Xizhi Zhu 的这篇Qt之美(一):D指针/私有实现,对于很多批评不美的同路人,暂且不去评论,只是想支持 ...
- ajax极简教程
一.什么是ajax ajax即异步JavaScript和XML,它是一种用于创建快速动态网页的技术.作用是通过在后台与服务器进行少量数据交换,使网页实现异步更新.这意味着可以在不重新加载整个网页的情况 ...
- WEB javaScript
javaScript 1.常规方法document.write("内容") :书写内容到网页中window.alert("内容") :网页警告弹窗 2.使用方法 ...