ThreadLocal深入理解 修订版
本文是传智博客多线程视频的学习笔记。
原版本见
http://blog.csdn.net/dlf123321/article/details/42531979
ThreadLocal是一个和线程安全相关的类。
它能干什么?
能保证在一个线程内,某个变量的全局共享。
说的很模糊,咱们看一个图
线程1里面的数据,应该在线程1范围内的模块a,b,c都能访问。
线程2里面的数据,应该在线程3范围内的模块a,b,c都能访问。
且线程1,2之间数据不会混淆。
那它有什么用呢?
举个例子,银行的转账包含两步,存款和取款,时候如果在存款取款中间出了问题,就得回滚;如果一切正常等整个交易完成了再commit,而调用commit的对象是Connection。那你说,如果多个线程共用一个Connection会发生什么问题?
一个非线程安全的例子
在我们讲述ThreadLocal之前,我们先看一个例子。
import java.util.Random; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadScopeDataShare { static private int data = 0; public static void main(String[] args) { ExecutorService threadPool = Executors.newCachedThreadPool(); for (int i = 0; i < 2; i++) { threadPool.execute(new Runnable() { @Override public void run() { int data2= new Random().nextInt(100); System.out.println(Thread.currentThread().getName()+" put "+data2); data=data2; try { Thread.sleep(1000); //为什么要睡1秒 大家懂吗? } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } new A().get(); new B().get(); } }); } threadPool.shutdown(); } public static int getData() { return data; } } class A { public int get() { int data = ThreadScopeDataShare.getData(); System.out .println("a "+Thread.currentThread().getName() + " getdata " + data); return data; } } class B { public int get() { int data = ThreadScopeDataShare.getData(); System.out .println("b "+Thread.currentThread().getName() + " getdata " + data); return data; } }
在我们设想中,应该是线程1放的数据,在线程1中,模块A与模块取得的数据应该是一致的。同理,线程2放的数据,与工作再线程2下的模块A模板取得的数据也应该是一致的。
可是上面的代码的运行结果却是:
pool-1-thread-1 put 90
pool-1-thread-2 put 78
a pool-1-thread-2 getdata 78
b pool-1-thread-2 getdata 78
a pool-1-thread-1 getdata 78
b pool-1-thread-1 getdata 78
改进版
我们新建一个map,key是当前线程,value是我们要保存的数据。
那么就可以保证每个线程的各个模块取得的数据都是一致的。
public class ThreadScopeShareData3 { private static Map<Thread, Integer> threadData = new HashMap<Thread, Integer>(); public static void main(String[] args) { ExecutorService threadPool = Executors.newCachedThreadPool(); for (int i = 0; i < 2; i++) { threadPool.execute(new Runnable() { @Override public void run() { int data2= new Random().nextInt(100); System.out.println(Thread.currentThread().getName()+" put "+data2); threadData.put(Thread.currentThread(),data2); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } new A().get(); new B().get(); } }); } threadPool.shutdown(); } static class A{ public void get(){ int data = threadData.get(Thread.currentThread()); System.out.println("A from " + Thread.currentThread().getName() + " get data :" + data); } } //省略class B }
运行结果
pool-1-thread-1 put 2
pool-1-thread-2 put 99
A from pool-1-thread-2 get data :99
A from pool-1-thread-1 get data :2
B from pool-1-thread-1 get data :2
B from pool-1-thread-2 get data :99
ThreadLocal的简单介绍
早在JDK 1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序。
当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
我们先看应用再讲原理,然后再讲一个实际的应用。
第一个应用
public class ThreadLocalTest { private static ThreadLocal<Integer> x = new ThreadLocal<Integer>(); 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(500); System.out.println(Thread.currentThread().getName() + " has put data :" + data); x.set(data); new A().get(); new B().get(); } }).start(); } } static class A{ public void get(){ int data = x.get(); System.out.println("A from " + Thread.currentThread().getName() + " get data :" + data); } } static class B{ public void get(){ int data = x.get(); System.out.println("B from " + Thread.currentThread().getName() + " get data :" + data); } } }
Thread-0 has put data :67
A from Thread-0 get data :67
B from Thread-0 get data :67
Thread-1 has put data :221
A from Thread-1 get data :221
B from Thread-1 get data :221
完全符合我们的要求。
这里有个问题:
如果一个线程能要共享多个变量怎么做?
private static ThreadLocal<Integer> x = new ThreadLocal<Integer>();
private static ThreadLocal<Integer> y = new ThreadLocal<Integer>();
不嫌麻烦吗?
ThreadLocal里可以放Interger,也可以放Objcet么。
如果几个变量有关系,如name,age我们就把它们包装成User;
如果变量没有关系,那就包装成一个map。
(当然一个线程如果要共享多个变量,那么分别设置为x,y也是可以的)
这样可以不?
import java.util.Random; public class ThreadLocalTest3 { private static ThreadLocal<MyThreadScopeData2> myThreadScopeData = new ThreadLocal<MyThreadScopeData2>(); 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(500); MyThreadScopeData2 myData = new MyThreadScopeData2(); myData.setName("name" + data); myData.setAge(data); myThreadScopeData.set(myData); new A().get(); new B().get(); } }).start(); } } static class A { public void get() { MyThreadScopeData2 myData = myThreadScopeData.get(); System.out .println("A from " + Thread.currentThread().getName() + " getMyData: " + myData.getName() + "," + myData.getAge()); } } //省略class B } class MyThreadScopeData2 { private static ThreadLocal<MyThreadScopeData2> map = new ThreadLocal<MyThreadScopeData2>(); private String name; private int age; //省略get set }
可以,不过对用户来说暴露了ThreadLocal的应用,我们希望在调用的时候,ThreadLocal对用户是透明的。
换句话说,我们得把ThreadLocal包装起来。
import java.util.Random; public class ThreadLocalTest2 { 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(500); MyThreadScopeData.getThreadInstance().setName("name" + data); MyThreadScopeData.getThreadInstance().setAge(data); new A().get(); new B().get(); } }).start(); } } static class A{ public void get(){ MyThreadScopeData myData = MyThreadScopeData.getThreadInstance(); System.out.println("A from " + Thread.currentThread().getName() + " getMyData: " + myData.getName() + "," + myData.getAge()); } } //省略Class B } class MyThreadScopeData{ private MyThreadScopeData(){} public static MyThreadScopeData getThreadInstance(){ MyThreadScopeData instance = map.get(); if(instance == null){ instance = new MyThreadScopeData(); map.set(instance); } return instance; } private static ThreadLocal<MyThreadScopeData> map = new ThreadLocal<MyThreadScopeData>(); private String name; private int age; //省略getset }
关于上面的单例模式可以参考
http://blog.csdn.net/lovelion/article/details/7420886
现在重头戏来了,看看ThreadLocal实现的原理。
参见:
http://blog.csdn.net/dlf123321/article/details/42531979
ThreadLocal深入理解 修订版的更多相关文章
- ThreadLocal深入理解二
转载:http://doc00.com/doc/101101jf6 今天在看之前转载的博客:ThreadLocal的内部实现原理.突然有个疑问, 按照threadLocal的原理, 当把一个对象存入到 ...
- ThreadLocal深入理解一
转载:http://www.cnblogs.com/dolphin0520/p/3920407.html 想必很多朋友对ThreadLocal并不陌生,今天我们就来一起探讨下ThreadLocal的使 ...
- Java中的ThreadLocal深入理解
提到ThreadLocal,有些Android或者Java程序员可能有所陌生,可能会提出种种问题,它是做什么的,是不是和线程有关,怎么使用呢?等等问题,本文将总结一下我对ThreadLocal的理解和 ...
- ThreadLocal的理解与应用场景分析
对于Java ThreadLocal的理解与应用场景分析 一.对ThreadLocal理解 ThreadLocal提供一个方便的方式,可以根据不同的线程存放一些不同的特征属性,可以方便的在线程中进行存 ...
- Python中ThreadLocal的理解与使用
一.对 ThreadLocal 的理解 ThreadLocal,有的人叫它线程本地变量,也有的人叫它线程本地存储,其实意思一样. ThreadLocal 在每一个变量中都会创建一个副本,每个线程都可以 ...
- java中threadlocal的理解
[TOC] #java中threadlocal的理解##一.threadlocal的生命周期和ThreadLocalMap的生命周期可以吧TreadLocal看做是一个map来使用,只不过这个map是 ...
- ThreadLocal简单理解
在java开源项目的代码中看到一个类里ThreadLocal的属性: private static ThreadLocal<Boolean> clientMode = new Thread ...
- threadlocal彻底理解,深刻
本文转自http://blog.csdn.net/huachao1001/article/details/51970237 ThreadLocal的使用相信大家都比较熟悉,但是ThreadLocal内 ...
- 我对ThreadLocal的理解
声明:小弟菜狗一个.对ThreadLocal的描写叙述和理解难免有所偏差 近期由于须要深入的了解android的handler消息机制而去查看了Looper的源代码.众所周知在主线程中是不须要在程序猿 ...
随机推荐
- Dynamics CRM2016 Web Api之根据时间查询数据
我的博文里已经有多次提到CRM中的时间处理问题了,本篇继续探讨在web api的场景下时间字段如何处理,本篇只涉及查询,针对2016中新增的时间行为"用户当地时间"和"无 ...
- Swift基础之侧滑Cell显示操作按钮
好久没写过博客了,今天展示一个UITableView基础的内容侧滑Cell的方法使用,之前写过OC语言的http://blog.csdn.net/hbblzjy/article/details/517 ...
- 递归dict
一个看起来非常酷的定义 class Example(dict): def __getitem__(self, item): try: return dict.__getitem__(self, ite ...
- Dynamics CRM2016 Web API之Create related entities in one operation
本篇继续来介绍两个web api的接口,一个是"Create related entities in one operation"即在一步操作中完成主实体的创建加关联实体的创建,一 ...
- CentOS6.7 下安装JDK
第一步:从官网上下载rpm版本的jdk文件. 第二步:安装JDK 执行命令rpm -ivh jdk-8u73-linux-i586.rpm 至此,JDK安装完成,我们来看下JDK的安装目录 ...
- Xcode8出现的一些常见问题
消除无用输出语句问题:Xcode8之后,新创建的项目在手机上运行后,就会在输出窗口,输出一大堆乱七八糟的日志,对我们几乎没有用,如图: 解决办法: [product]-[scheme]-[Edit S ...
- x264源代码简单分析:编码器主干部分-2
===================================================== H.264源代码分析文章列表: [编码 - x264] x264源代码简单分析:概述 x26 ...
- 【伯乐在线】最值得阅读学习的 10 个 C 语言开源项目代码
原文出处: 平凡之路的博客 欢迎分享原创到伯乐头条 伯乐在线注:『阅读优秀代码是提高开发人员修为的一种捷径』http://t.cn/S4RGEz .之前@伯乐头条 曾发过一条微博:『C 语言进阶有 ...
- linux配置java环境变量 转过几个,这个最详细和靠谱
一. 解压安装jdk 在shell终端下进入jdk-6u14-linux-i586.bin文件所在目录,之后会在当前目录下生成一个jdk1.6.0_14目录二. 需要配置的环境变量 1. PATH环境 ...
- Android初级教程通过简要分析“土司”源码,来自实现定义土司理论探讨
由于系统自带的土司瞬间即逝,而且非常难看.因此我们就希望自定义自己的土司风格.有些实例就是基于自定义土司完成的,例如金山卫士的火箭发射,基本原理就是个土司.但是在做出自己的土司风格之前,还是要简要分析 ...