线程系列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 ...
随机推荐
- CTF 常见操作总结
一般流程 首先看header, veiwsource, 目录扫描 有登陆, 尝试sql注入&爆破 有数据库, 必然sql注入? 普通sql注入 判断是否存在回显异常 尝试单双引号 查是字符型? ...
- 不再显示广告案例(php操作cookie)
1,页面简单结构搭建 ad.html <!DOCTYPE html> <html lang="en"> <head> <meta char ...
- JS中数组与对象的遍历方法实例小结
一.数组的遍历: 首先定义一个数组 1 arr=['snow','bran','king','nightking']; 1.for循环,需要知道数组的长度; 2.foreach,没有返回值,可以不知道 ...
- JavaJDBC【一、概述】
其实这个内容在学习java基础的时候就有看过了,只是没有详细整理,在这再整理一下 数据库操作对于任何一门后端语言来说都是很重要的 JDBC:Java Data Base Connectivity 内容 ...
- Vivotek 摄像头远程栈溢出漏洞分析及利用
Vivotek 摄像头远程栈溢出漏洞分析及利用 近日,Vivotek 旗下多款摄像头被曝出远程未授权栈溢出漏洞,攻击者发送特定数据可导致摄像头进程崩溃. 漏洞作者@bashis 放出了可造成摄像头 C ...
- STM32WB AHB总线、APB总线与外设
方框图: 如图所示: 1)APB1外设 2)APB2外设 3)AHB1外设 4)AHB2外设 5)AHB3外设 6)AHB4外设(ABH共享总线外设) 内存映射关系图:
- 8.6.zookeeper应用案例_分布式共享锁的简单实现
1.分布式共享锁的简单实现 在分布式系统中如何对进程进行调度,假设在第一台机器上挂载了一个资源,然后这三个物理分布的进程都要竞争这个资源,但我们又不希望他们同时 进行访问,这时候我们就需要一个协调器, ...
- 白话跨域CORS
跨域访问控制是浏览器和服务器按照约定,协同工作,守护安全的一种机制. 其中认为浏览器和服务器是安全的,但是浏览器上运行的页面(HTML+JS)可能不安全. 分几种不同方式. 页面跨域简单请求(Get/ ...
- java线程基础巩固---数据同步引入并结合jconsole,jstack以及汇编指令认识synchronized关键字
对于多线程编程而言其实老生成谈的就是数据同步问题,接下来就会开始接触这块的东东,比较麻烦,但是也是非常重要,所以按部就班的一点点去专研它,下面开始. 数据同步引入: 这里用之前写过的银行叫号的功能做为 ...
- openGL中的gl,glu,glut
OpenGL函数库相关的API有核心库(gl).实用库(glu).辅助库(aux).实用工具库(glut).窗口库(glx.agl.wgl)和扩展函数库等.gl是核心,glu是对gl的部分封装.glx ...