ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。JDK 1.2的版本中就提供java.lang.ThreadLocal,使用这个工具类可以很简洁地编写出优美的多线程程序,ThreadLocal并不是一个Thread,而是Thread的局部变量。

1.下图和辅助代码解释ThreadLocal的作用和目的:用于实现线程内的数据共享,即对于相同的程序代码,多个模块在同一个线程中运行时要共享一份数据,而在另外线程中运行时又共享另外一份数据。

2.每个线程调用全局ThreadLocal对象的set方法,就相当于往其内部的map中增加一条记录,key分别是各自的线程,value是各自的set方法传进去的值。在线程结束时可以调用ThreadLocal.clear()方法,这样会更快释放内存,不调用也可以,因为线程结束后也可以自动释放相关的ThreadLocal变量。

3.ThreadLocal的应用场景:
      (1)订单处理包含一系列操作:减少库存量、增加一条流水台账、修改总账,这几个操作要在同一个事务中完成,通常也即同一个线程中进行处理,如果累加公司应收款的操作失败了,则应该把前面的操作回滚,否则,提交所有操作,这要求这些操作使用相同的数据库连接对象,而这些操作的代码分别位于不同的模块类中。
      (2)银行转账包含一系列操作: 把转出帐户的余额减少,把转入帐户的余额增加,这两个操作要在同一个事务中完成,它们必须使用相同的数据库连接对象,转入和转出操作的代码分别是两个不同的帐户对象的方法。
      (3)例如Strut2的ActionContext,同一段代码被不同的线程调用运行时,该代码操作的数据是每个线程各自的状态和数据,对于不同的线程来说,getContext方法拿到的对象都不相同,对同一个线程来说,不管调用getContext方法多少次和在哪个模块中getContext方法,拿到的都是同一个。
  4.实验案例:定义一个全局共享的ThreadLocal变量,然后启动多个线程向该ThreadLocal变量中存储一个随机值,接着各个线程调用另外其他多个类的方法,这多个类的方法中读取这个ThreadLocal变量的值,就可以看到多个类在同一个线程中共享同一份数据。
  5.实现对ThreadLocal变量的封装,让外界不要直接操作ThreadLocal变量。
      (1)对基本类型的数据的封装,这种应用相对很少见。
      (2)对对象类型的数据的封装,比较常见,即让某个类针对不同线程分别创建一个独立的实例对象。

例子程序:

第一种实现方式:(但是这种实现方式不如第二种实现方式好)

package cn.itcast.lishehe;

import java.util.Random;
/** 李社河-2015年6月11日
* 题目要求:构造两线程,要求:
* (1)两线程并发操作 (这就要求不能使用syschronized关键字)
* (2)要求两线程分别访问各自的数据MyData对象,互不干扰
* (这里就可以使用ThreadLocal对象,通过set()和get()即可获得与本线程相关的MyData对象,但注意,其只能关联一个数据,
* 所以对于多个数据则应该封装到一个类中,其实际上也是通过Map实现的)
* (3)线程内有A、B两个模块,模块之间共享数据MyData数据
*
* 本程序实现方式不如ThreadLocalDataIndependent2.java好。
**/
public class ThreadLocalDataIndependent1 {
static ThreadLocal<MyData> threadLocal = new ThreadLocal<MyData>();
public static void main(String[] args) {
for (int i = 0; i < 2; i++) {
new Thread(new Runnable() {
@Override
public void run() {
int data = new Random().nextInt(); //这里的data必须定义为局部变量,否则线程间不能实现数据独立
MyData myData = new MyData();
myData.setName("name"+data);
myData.setAge(data);
threadLocal.set(myData);
new A().get();
new B().get();
}
}).start();
}
} static class A{
public void get(){
MyData myData = threadLocal.get();
System.out.println("A from "+Thread.currentThread().getName()+" get MyData :"
+myData.getName()+","+myData.getAge());
}
}
static class B{
public void get(){
MyData myData = threadLocal.get();
System.out.println("B from "+Thread.currentThread().getName()+" get MyData :"
+myData.getName()+","+myData.getAge());
}
}
}
class MyData{
private String name;
private int age;
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;
} }

运行结果

第二种实现方式:(struts2中对于用户的每次请求创建一个action实例,其也是这样实现的)

package cn.itcast.lishehe;

import java.util.Random;
/** 李社河-2015年6月11日
* 题目要求:构造两线程,要求:
* (1)两线程并发操作 (这就要求不能使用syschronized关键字)
* (2)要求两线程分别访问各自的数据MyData对象,互不干扰
* (这里就可以使用ThreadLocal对象,通过set()和get()即可获得与本线程相关的MyData对象,但注意,其只能关联一个数据,
* 所以对于多个数据则应该封装到一个类中,其实际上也是通过Map实现的)
* (3)线程内有A、B两个模块,模块之间共享数据MyData数据
*
* 本程序实现方式比ThreadLocalDataIndependent1.java要好。
* 其中Struts2对于用户的每次请求,都将创建一个action实例进行处理,每个线程都有其独立的数据,其实现方式就是这种。
**/
public class ThreadLocalDataIndependent2 {
public static void main(String[] args) {
for (int i = 0; i < 2; i++) {
new Thread(new Runnable() {
@Override
public void run() {
int data = new Random().nextInt(); //这里的data必须定义为局部变量,否则线程间不能实现数据独立
MyData2.getInstance().setName("name"+data);
MyData2.getInstance().setAge(data);
new A().get();
new B().get();
}
}).start();
}
} static class A{
public void get(){
MyData2 myData = MyData2.getInstance();
System.out.println("A from "+Thread.currentThread().getName()+" get MyData :"
+myData.getName()+","+myData.getAge());
}
}
static class B{
public void get(){
MyData2 myData = MyData2.getInstance();
System.out.println("B from "+Thread.currentThread().getName()+" get MyData :"
+myData.getName()+","+myData.getAge());
}
}
}
class MyData2{
private static ThreadLocal<MyData2> threadMap = new ThreadLocal<MyData2>();
private MyData2(){ }
//这里无需使用syschronized关键字
public static MyData2 getInstance(){
MyData2 myData = threadMap.get();
if(myData==null){
myData = new MyData2();
threadMap.set(myData);
}
return myData;
} private String name;
private int age;
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;
} }

运行结果

多线程(三) 实现线程范围内模块之间共享数据及线程间数据独立(ThreadLocal)的更多相关文章

  1. 多线程(四) 实现线程范围内模块之间共享数据及线程间数据独立(Map集合)

    多个线程访问共享对象和数据的方式 1.如果每个线程执行的代码相同,可以使用同一个Runnable对象,这个Runnable对象中有那个共享数据,例如,买票系统就可以这么做. 2.如果每个线程执行的代码 ...

  2. {Python之线程} 一 背景知识 二 线程与进程的关系 三 线程的特点 四 线程的实际应用场景 五 内存中的线程 六 用户级线程和内核级线程(了解) 七 python与线程 八 Threading模块 九 锁 十 信号量 十一 事件Event 十二 条件Condition(了解) 十三 定时器

    Python之线程 线程 本节目录 一 背景知识 二 线程与进程的关系 三 线程的特点 四 线程的实际应用场景 五 内存中的线程 六 用户级线程和内核级线程(了解) 七 python与线程 八 Thr ...

  3. 多线程篇四:ThreadLocal实现线程范围内变量共享

    1.static实现线程范围内变量共享 package com.test.shareData; import java.util.Random; /** * 多线程范围内的数据共享 * @author ...

  4. 线程系列4--Java线程范围内的共享数据(一)

    这张图片是我看传智播客的视频时的截屏,这个图片很直观的展示了线程范围内的数据共享.当同一个线程在执行三个不同业务模块时,这三个业务模块访问的数据是共享的.更直白的说,当一个执行线索在穿个每个业务模块时 ...

  5. clang的线程安全分析模块 thread safety analysis

    介绍 Clang的线程安全分析模块是C++语言的一个扩展,能对代码中潜在的竞争条件进行警告.这种分析是完全静态的(即编译时进行),没有运行时的消耗.当前这个功能还在开发中,但它已经具备了足够的成熟度, ...

  6. C# .Net 多进程同步 通信 共享内存 内存映射文件 Memory Mapped 转 VC中进程与进程之间共享内存 .net环境下跨进程、高频率读写数据 使用C#开发Android应用之WebApp 分布式事务之消息补偿解决方案

    C# .Net 多进程同步 通信 共享内存 内存映射文件 Memory Mapped 转 节点通信存在两种模型:共享内存(Shared memory)和消息传递(Messages passing). ...

  7. twsited(4)--不同模块用redis共享以及用web发送数据到tcpserver

    上一章开头我们说,要连接之前flask系列文章中的用户,结果篇幅不够,没有实现. 今天我们把它实现一下.话说,不同模块之间,该如何联系在一起,通常都是mysql.redis.rabbitmq还有RPC ...

  8. Java多线程——线程范围内共享变量

    多个线程访问共享对象和数据的方式 1.如果每个线程执行的代码相同,可以使用同一个Runnable对象,这个Runnable对象中有那个共享数据,例如,买票系统就可以这么做. package java_ ...

  9. Java多线程——线程范围内共享变量和ThreadLocal

    多个线程访问共享对象和数据的方式 1.如果每个线程执行的代码相同,可以使用同一个Runnable对象,这个Runnable对象中有那个共享数据,例如,买票系统就可以这么做. package java_ ...

随机推荐

  1. Myeclipse修改设置Default VM Arguments

    打开Windows-> Preferences 然后选择右侧菜单的Java->Installed JREs 点击右侧的jdk,然后点击"Edit"按钮 Default ...

  2. Thread源码剖析

    前言 昨天已经写了: 多线程三分钟就可以入个门了! 如果没看的同学建议先去阅读一遍哦~ 在写文章之前通读了一遍<Java 核心技术 卷一>的并发章节和<Java并发编程实战>前 ...

  3. Jenkins配置Gogs webhook插件

    前言 我们在前面使用Jenkins集合Gogs来进行持续集成的时候,选择的是Jenkins定时检测git仓库是否有更新来决定是否构建.也就是说,我们提交了代码Jenkins并不会马上知道,那么我们可以 ...

  4. [LeetCode] Cut Off Trees for Golf Event 为高尔夫赛事砍树

    You are asked to cut off trees in a forest for a golf event. The forest is represented as a non-nega ...

  5. django 模板继承与重写

    1.模板的继承一般用在别人给我们做好的HTML页面,当我们发现有很多的页面都具有相同的部分,这会我们应该考虑怎么能把他们相同的部分给提取出来,提取出来的部分我们作为一个单独的HTML文件叫做base. ...

  6. Virtual Box下虚拟机复制后ip地址重复

    通过桥接模式上网的虚拟机在复制之后,出现三台机器的ip地址都是一样的,还都可以上网, 主要是因为在复制的时候,把网卡信息啥的都一起复制了, 为了设置为不同的ip,需要修改复制后的机器的mac地址. 首 ...

  7. Trie模版

    struct Trie{ Trie* nxt[]; int v; Trie(){ ;i<;i++){ nxt[i]=NULL; } v=-; } void insert(char s[],int ...

  8. 2015 多校联赛 ——HDU5402(模拟)

    For each test case, in the first line, you should print the maximum sum. In the next line you should ...

  9. 如何理解Spring IOC

    Spring IOC 思维导图 要了解控制反转( Inversion of Control ), 我觉得有必要先了解软件设计的一个重要思想:依赖倒置原则(Dependency Inversion Pr ...

  10. Go 实现字符串相似度计算函数 Levenshtein 和 SimilarText

    [转]http://www.syyong.com/Go/Go-implements-the-string-similarity-calculation-function-Levenshtein-and ...