线程系列5--java中的ThreadLocal类实现线程范围内的数据共享(二)
ThreadLocal类可以理解成一个类似与map集合使用,以当前线程当做key 来使用,将线程氛围内需要共享的数据当做value,形成键值对的形式使用。ThreadLocal和线程同步机制都是为了解决多线程中对同一个变量的访问冲突问题。
在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量。这时该变量是多个线程共享的,使用同步机制要求程序慎密地分析什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放对象锁等繁杂的问题,程序设计和编写难度相对较大。而ThreadLocal则从另一个角度来解决多线程的并发访问。ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。概括起来说,对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。
我还是以传智播客中的代码举例说明:下面代码是利用ThreadLocal来实现多线程的数据共享,代码很简单不做过多讲解。
/**
* ThreadLocal 类似于map集合 只是集合的key是当前正在运行的线程
* 通过ThreadLocal可以将变量(或者是变量的容器)与当前线程相绑定.
*/
private static ThreadLocal<Integer> threadLocal = 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();
threadLocal.set(data) ; //将数据绑定到带当前线程
System.out.println(Thread.currentThread().getName()+ " put random data:"+data);
new A().get() ;
new B().get() ;
} }).start() ;
}
}
static class A {
public void get(){
//取数据都从当前线程中取得
int d = threadLocal.get() ;
System.out.println("A from " + Thread.currentThread().getName()
+ " get data :" + d);
}
}
static class B{
public void get(){
//取数据都从当前线程中取得
int d = threadLocal.get() ;
System.out.println("B from " + Thread.currentThread().getName()
+ " get data :" + d);
}
}
当线程中要存放多个对象时怎么办那,显然我们要封装对象,将对象放进ThreadLocal中,此处不再书写相关代码了,我们只需要将封装的对象直接当做泛型放到ThreadLocal中即可,非常易懂;
代码继续升级改造,传智播客中是利用单例模式的思想,将我们要封装的对象提出来给与ThreadLocal封装。不过我觉得张孝祥老师在讲解时使用单例模式不太好,单例模式是在系统中只有一份,但是ThreadLocal是每个线程复制一个对象,是典型的利用空间换时间,每个线程都会对应一个封装的对象,并不想单例模式那样。
private static ThreadLocal<MyThreadLocal> threadLocal = new ThreadLocal<MyThreadLocal>();
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();
MyThreadLocal.getMyThreadLocalInstant().setName("傲视苍穹--大泼猴-孙悟空"+data);
MyThreadLocal.getMyThreadLocalInstant().setValue("傲视苍穹--大泼猴-杨婵"+data);
System.out.println(Thread.currentThread().getName()+ " put random data:"+data);
new A().get() ;
new B().get() ;
} }).start() ;
}
}
static class A {
public void get(){
//取数据都从当前线程中取得
MyThreadLocal myThreadLocal = MyThreadLocal.getMyThreadLocalInstant();
System.out.println("A from " + Thread.currentThread().getName()
+ " name :" + myThreadLocal.getName()+"value:"+myThreadLocal.getValue());
}
}
static class B{
public void get(){
//取数据都从当前线程中取得
MyThreadLocal myThreadLocal = MyThreadLocal.getMyThreadLocalInstant();
System.out.println("B from " + Thread.currentThread().getName()
+ " name :" + myThreadLocal.getName()+"value:"+myThreadLocal.getValue());
}
}
static class MyThreadLocal{
private MyThreadLocal(){}
//单例模式:线程安全
// private static MyThreadLocal myThreadLocal = null;
// public static synchronized MyThreadLocal getMyThreadLocalInstant(){
// if(myThreadLocal == null){
// myThreadLocal = new MyThreadLocal();
// }
// return myThreadLocal;
// }
//使用单例模式的思想,来实现对象创建的思想
private static ThreadLocal<MyThreadLocal> map = new ThreadLocal<>(); public static MyThreadLocal getMyThreadLocalInstant(){
if(map.get() == null){
map.set(new MyThreadLocal());
}
return map.get();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
private String name;
private String value;
}
上述代码利用单例模式的思想来管理ThreadLocal对象的创建,但是这样并不是说整个内存中只有一个对象,大家千万理解清楚,我听视频的时候就被这点迷惑了,这里只是用单例模式的思想来管理ThreadLocal对象的创建,每个线程都会创建一个被共享的数据对象放到ThreadLocal中。
下面内容摘自知乎用户,链接:https://www.zhihu.com/question/23089780/answer/62097840
可以总结为一句话:ThreadLocal的作用是提供线程内的局部变量,这种变量在线程的生命周期内起作用,减少同一个线程内多个函数或者组件之间一些公共变量的传递的复杂度。
举个例子,我出门需要先坐公交再做地铁,这里的坐公交和坐地铁就好比是同一个线程内的两个函数,我就是一个线程,我要完成这两个函数都需要同一个东西:公交卡(北京公交和地铁都使用公交卡),那么我为了不向这两个函数都传递公交卡这个变量(相当于不是一直带着公交卡上路),我可以这么做:将公交卡事先交给一个机构,当我需要刷卡的时候再向这个机构要公交卡(当然每次拿的都是同一张公交卡)。这样就能达到只要是我(同一个线程)需要公交卡,何时何地都能向这个机构要的目的。
有人要说了:你可以将公交卡设置为全局变量啊,这样不是也能何时何地都能取公交卡吗?但是如果有很多个人(很多个线程)呢?大家可不能都使用同一张公交卡吧(我们假设公交卡是实名认证的),这样不就乱套了嘛。现在明白了吧?这就是ThreadLocal设计的初衷:提供线程内部的局部变量,在本线程内随时随地可取,隔离其他线程。
线程系列5--java中的ThreadLocal类实现线程范围内的数据共享(二)的更多相关文章
- java之线程(线程的创建方式、java中的Thread类、线程的同步、线程的生命周期、线程之间的通信)
CPU:10核 主频100MHz 1核 主频 3GHz 那么哪一个CPU比较好呢? CPU核不是越多越好吗?并不一定.主频用于衡量GPU处理速度的快慢,举个例子10头牛运送货物快还是1架飞机运 ...
- Java中的Collections类
转载:https://blog.csdn.net/yangxingpa/article/details/80515963 从[Java]Java中的Collections类——Java中升级版的数据结 ...
- Java中的Unsafe类111
1.Unsafe类介绍 Unsafe类是在sun.misc包下,不属于Java标准.但是很多Java的基础类库,包括一些被广泛使用的高性能开发库都是基于Unsafe类开发的,比如Netty.Hadoo ...
- 【Java并发】Java中的原子操作类
综述 JDK从1.5开始提供了java.util.concurrent.atomic包. 通过包中的原子操作类能够线程安全地更新一个变量. 包含4种类型的原子更新方式:基本类型.数组.引用.对象中字段 ...
- 【Java】Java中的Collections类——Java中升级版的数据结构【转】
一般来说课本上的数据结构包括数组.单链表.堆栈.树.图.我这里所指的数据结构,是一个怎么表示一个对象的问题,有时候,单单一个变量声明不堪大用,比如int,String,double甚至一维数组.二维数 ...
- Java中的魔法类-Unsafe
Unsafe是位于sun.misc包下的一个类,主要提供一些用于执行低级别.不安全操作的方法,如直接访问系统内存资源.自主管理内存资源等,这些方法在提升Java运行效率.增强Java语言底层资源操作能 ...
- java中的Atomic类
文章目录 问题背景 Lock 使用Atomic java中的Atomic类 问题背景 在多线程环境中,我们最常遇到的问题就是变量的值进行同步.因为变量需要在多线程中进行共享,所以我们必须需要采用一定的 ...
- 关于Java中的String类知识点小总结
Java中的String类知识点 前言 在 Java 中字符串属于对象,Java 提供了 String 类来创建和操作字符串. 如何创建字符串 最简单的方式 String str = "he ...
- java中的原子操作类AtomicInteger及其实现原理
/** * 一,AtomicInteger 是如何实现原子操作的呢? * * 我们先来看一下getAndIncrement的源代码: * public final int getAndIncremen ...
随机推荐
- C#取绝对值函数
System.Math.Abs(float value); System.Math.Abs(decimal value); System.Math.Abs(int value); System.Mat ...
- (四)创建基于maven的javaFX+springboot项目,用户界面与后台逻辑分离方式
下面来介绍创建maven的javaFX+springboot项目,基于用户界面与后天逻辑分离的方式,用户界面使用fxml文件来常见,类似于jsp,可以引入css文件修饰界面 maven依赖 <d ...
- ES6入门二:默认值与默认值表达式
默认值 默认值表达式 需要注意的是,这种默认值和默认表达式在IE的最新版本中仍然没有得到兼容,只能通过编译转码的方式降级到ES5使用. 一.默认值 在函数声明时可以给形参赋默认值,当调用函数时不传入或 ...
- 99乘法表(js)
//九九乘法表 let i,j,str; for(i=1;i<=9;i++) { str = ""; for(j=1;j<=i;j++) { str = str+i+' ...
- JavaJDBC【六、连接池】
背景 1.连接是一种重要资源 2.频繁连接数据库会增加数据库压力 常用连接池 dbcp 1.导入jar包(官网下载) commons-dbcp.jar commons-pool.jar commons ...
- ZooKeeper--动物管理员
复杂的软件集群系统从来绕不开高可用.负载均衡等问题,大数据系统更是如此. 高可用:计算机系统的可用性定义为系统保持正常运行时间的百分比,具体手段有自动检测,自动切换,自动恢复等. 负载均衡:主要解决单 ...
- Bridge的数据在内核处理流程
转:http://blog.sina.com.cn/s/blog_67cc0c8f0101oh33.html 转载一篇Bridge的数据在内核处理流程,文章写的不错啊! (2013-07-05 16: ...
- 上班时能不能戴耳机?V
上班时能不能戴耳机? 新入职一公司, 上班时间不能戴耳机在V站一石激起千层浪,网友意见主要分几派: 甩手走人型: 神经病公司,这还不赶紧走 不走等着过年 不走留着转正 离职,下一题 还是赶紧离职吧 这 ...
- yocto 编译C程序
1. 找到编译器位置所在(相关设置参考/opt/poky/1.7/environment-setup-cortexa9hf-vfp-neon-poky-linux-gnueabi文件) poky安装在 ...
- zabbix 3.2.2 agent端(源码包)安装部署 (二)
一.zabbix agent 端安装部署 1.创建zabbix用户和组 # groupadd zabbix # useradd -g zabbix zabbix -s /sbin/nologin 2. ...