线程系列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 ...
随机推荐
- 向PHP使用Post方式上传文件
欢迎访问我的个人博客,获取更多有用的东西 链接一 链接二 也可以关注我的微信订阅号:CN丶Moti 1.post-file.html form表单提交方式一定要是post,而且添加属性enctype= ...
- ui组件库
基于Vue的Quasar Framework 中文网 http://www.quasarchs.com/ quasarframework/quasar: Quasar Frameworkhttps:/ ...
- 短信对接——一种jdbc链接运用
package sms; import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamRead ...
- Flutter 之页面状态保持
一般情况下,我们使用tab切换的时候希望操作完毕之后,能够记住上个页面的状态, 但是使用Flutter的BottomNavigationBar的 时候默认是不记录页面状态的,即切换页面会导致重新加载. ...
- vue 日常开发小细节
1. element-ui 日期选区禁用,设置属性 disabledDate: (time) => { const curDate = (new Date()).getTime() const ...
- Repeater循环页面上的控件
List<string> list = new List<string>(); for (int k = 0; k < RepeaterList.Items.Count; ...
- [转]DELL PERC 系列阵列卡选型和用法指南
引用地址 https://www.sulabs.net/?p=895 DELL PERC 系列阵列卡选型和用法指南 2018年12月29日 Su 本文缘起于一位朋友在生产服务器硬件中,使用了错误的阵列 ...
- win10 修改文件夹右击默认打开程序
1.注册表打开 cmd regedit 2.打开如下位置 3.编辑图中2个Anycode.command的值 为打开 保存即可
- 蓝牙App漏洞系列分析之一CVE-2017-0601
蓝牙App漏洞系列分析之一CVE-2017-0601 0x01 概要 2017年5月的 Android 安全公告修复了我们提交的一个蓝牙提权中危漏洞,这个漏洞尽管简单,但比较有意思,能够使本地恶意 A ...
- python常用模块:标准文件及模块练习
1.请写出规范目录 并解释各文件夹的作用 bin 执行文件core 核心业务逻辑conf 配置文件lib 库.公共代码.第三方模块db 数据分析log 日志文件readme 文本文档 2.改造atm+ ...