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类实现线程范围内的数据共享(二)的更多相关文章

  1. java之线程(线程的创建方式、java中的Thread类、线程的同步、线程的生命周期、线程之间的通信)

    CPU:10核 主频100MHz 1核  主频    3GHz 那么哪一个CPU比较好呢? CPU核不是越多越好吗?并不一定.主频用于衡量GPU处理速度的快慢,举个例子10头牛运送货物快还是1架飞机运 ...

  2. Java中的Collections类

    转载:https://blog.csdn.net/yangxingpa/article/details/80515963 从[Java]Java中的Collections类——Java中升级版的数据结 ...

  3. Java中的Unsafe类111

    1.Unsafe类介绍 Unsafe类是在sun.misc包下,不属于Java标准.但是很多Java的基础类库,包括一些被广泛使用的高性能开发库都是基于Unsafe类开发的,比如Netty.Hadoo ...

  4. 【Java并发】Java中的原子操作类

    综述 JDK从1.5开始提供了java.util.concurrent.atomic包. 通过包中的原子操作类能够线程安全地更新一个变量. 包含4种类型的原子更新方式:基本类型.数组.引用.对象中字段 ...

  5. 【Java】Java中的Collections类——Java中升级版的数据结构【转】

    一般来说课本上的数据结构包括数组.单链表.堆栈.树.图.我这里所指的数据结构,是一个怎么表示一个对象的问题,有时候,单单一个变量声明不堪大用,比如int,String,double甚至一维数组.二维数 ...

  6. Java中的魔法类-Unsafe

    Unsafe是位于sun.misc包下的一个类,主要提供一些用于执行低级别.不安全操作的方法,如直接访问系统内存资源.自主管理内存资源等,这些方法在提升Java运行效率.增强Java语言底层资源操作能 ...

  7. java中的Atomic类

    文章目录 问题背景 Lock 使用Atomic java中的Atomic类 问题背景 在多线程环境中,我们最常遇到的问题就是变量的值进行同步.因为变量需要在多线程中进行共享,所以我们必须需要采用一定的 ...

  8. 关于Java中的String类知识点小总结

    Java中的String类知识点 前言 在 Java 中字符串属于对象,Java 提供了 String 类来创建和操作字符串. 如何创建字符串 最简单的方式 String str = "he ...

  9. java中的原子操作类AtomicInteger及其实现原理

    /** * 一,AtomicInteger 是如何实现原子操作的呢? * * 我们先来看一下getAndIncrement的源代码: * public final int getAndIncremen ...

随机推荐

  1. SQL SERVER中Datetime时间的范围与.net的DateTime对象的区别

    对于编写.net程序中我们一般写默认的时间,我们会自动创建一个new DateTime()对象.但与SQL SERVER连用我们就会出现一个时间范围的问题. 今天我就记录一下该时间问题. 我们创建的n ...

  2. 搭建自己的框架WedeNet(二)

    WedeNet2018.Infrastructure-基础设施层:结构如下: Tools结构如下: 考虑到系统可能会有多个数据上下文(暂时以两个为例),所以根据需要定义两个T4模板用来生成对应的ent ...

  3. luogu题解P2486[SDOI2011]染色--树链剖分+trick

    题目链接 https://www.luogu.org/problemnew/show/P2486 分析 看上去又是一道强行把序列上问题搬运到树上的裸题,然而分析之后发现并不然... 首先我们考虑如何在 ...

  4. 深入理解hive之事务处理

    事务的四个特性 1.automicity:原子性 2.consistency:一致性 3. isolation:独立性 4.durability:持久性 5.支持事务有几个条件需要满足:1.所有的事务 ...

  5. 在线程中使用ClientQuery注意的问题

    今天遇到奇怪的问题,在线程中建立一个TkbmMWClientQuery的临时对象q,及一个TkbmMWBinaryStreamFormat的临时对象bsf,第一次执行正常,再次执行时一直等待,也不产生 ...

  6. scroll js 原生

    1.当前位置滚动: document.documentElement.scrollTop 当前位置: 有可能是0 window.scrollTo(,document.documentElement.s ...

  7. API开发之接口安全(一)----生成sign

    在对于API的开发中 最让人头疼的 就是接口数据暴露 让一些有心之人 抓包之后恶意请求 那么如何解决这一弊端呢?自然而然的 我们就想到了 加密  那我们又如何加密 如何解密 才能使之有最安全的效率呢? ...

  8. Zookeeper01——zk的基本信息和安装

    一.Zookeeper的基本信息 1.1背景 无论在前面,我们学习hdfs,还是学习redis集群,我们都会使用到一个zookeeper进行选举.这导致了Redis的产生. 我们知道,在先前我们使用Z ...

  9. 认识并初步应用GitHub

    好好学习,天天向上 一.这是一个简单的开头 GIT的地址 https://github.com/Notexcellent GIT的用户名 Notexcxllent 学号后五位 82405 博客地址 h ...

  10. ubuntu学习笔记-tar 解压缩命令详解(转)

    tar 解压缩命令详解 -c: 建立压缩档案 -x:解压-t:查看内容-r:向压缩归档文件末尾追加文件-u:更新原压缩包中的文件 这五个是独立的命令,压缩解压都要用到其中一个,可以和别的命令连用但只能 ...