一、概念

可以将每个线程用到的数据与对应的线程号存放到一个map集合中,使用数据时从这个集合中根据线程号获取对应线程的数据,就可以实现线程范围内共享相同的变量。

二、代码

Runnable中的run()方法里面执行Thread.currentThread()都会对应当前Runnable对应的线程,因此A、B中对应的Thread.currentThread()对应所在的Runnable对应的线程

public class ThreadScopeShareData {

    private static Map<Thread, Integer> threadData = new HashMap<Thread, 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();
System.out.println(Thread.currentThread().getName()
+ " has put data :" + data);
threadData.put(Thread.currentThread(), data);
new A().get();
new B().get();
}
}).start();
}
} static class A{
public void get(){
int data = threadData.get(Thread.currentThread());
System.out.println("A from " + Thread.currentThread().getName()
+ " get data :" + data);
}
} static class B{
public void get(){
int data = threadData.get(Thread.currentThread());
System.out.println("B from " + Thread.currentThread().getName()
+ " get data :" + data);
}
}
}

三、ThreadLocal

JDK1.5提供了ThreadLocal类来方便实现线程范围内的数据共享,它的作用就相当于前面中的Map(内部并不是Map),也就是让每个线程拥有自己的值
一个ThreadLocal对象只能记录一个线程内部的一个共享变量,需要记录多个共享数据,可以创建多个ThreadLocal对象,或者将这些数据进行封装,将封装后的数据对象存入ThreadLocal对象中。
线程结束后也可以自动释放相关的ThreadLocal变量,也可以调用ThreadLocal.remove()方法用来更快释放内存。

代码:

public class ThreadLocalTest {  

    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(100);
System.out.println(Thread.currentThread().getName()+" has put data: "+data);
//往local里面设置值
threadLocal.set(data);
new A().get();
new B().get();
}
}).start();
}
} static class A{
public void get(){
int data =threadLocal.get();
System.out.println("A from "+Thread.currentThread().getName()+" has get data: "+data);
}
} static class B{
public void get(){
int data =threadLocal.get();
System.out.println("B from "+Thread.currentThread().getName()+" has get data: "+data);
}
}
}

假设需要保存不止一个值,可以把其他属性的值打包成一个类,然后将该类设置成ThreadLocal的值。

下面代码中,在类MyThreadLocalScopeDate里面定义了一个静态变量Map,用来保存所有线程创建的MyThreadLocalScopeDate,并使用单例使得不管多少线程都只创建一个MyThreadLocalScopeDate对象。

public class ThreadLocalTest {  

    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(100);
System.out.println(Thread.currentThread().getName()+" has put data: "+data);
//往local里面设置值
threadLocal.set(data);
//获取自己线程的MyThreadLocalScopeDate实例对象
MyThreadLocalScopeDate myData = MyThreadLocalScopeDate.getThreadInstance();
myData.setName("name"+data);
myData.setAge(data);
new A().get();
new B().get();
}
}).start();
}
} static class A{
public void get(){
int data =threadLocal.get();
System.out.println("A from "+Thread.currentThread().getName()+" has get data: "+data);
MyThreadLocalScopeDate myData = MyThreadLocalScopeDate.getThreadInstance();
System.out.println("A from "+Thread.currentThread().getName()+" has get MyThreadLocalScopeDate name: "+myData.getName()+" , age: "+myData.getAge());
}
} static class B{
public void get(){
int data =threadLocal.get();
System.out.println("B from "+Thread.currentThread().getName()+" has get data: "+data);
MyThreadLocalScopeDate myData = MyThreadLocalScopeDate.getThreadInstance();
System.out.println("B from "+Thread.currentThread().getName()+" has get MyThreadLocalScopeDate name: "+myData.getName()+" , age: "+myData.getAge());
}
}
} class MyThreadLocalScopeDate{//单例模式 private MyThreadLocalScopeDate(){};//构造方法私有化
private static ThreadLocal<MyThreadLocalScopeDate> map = new ThreadLocal<MyThreadLocalScopeDate>();//封装MyThreadLocalScopeDate是线程实现范围内共享 //思考AB两个线程过来的情况 自己分析 AB都需要的自己的对象 没有关系 所以不需要同步 如果有关系就需要同步了
public static /*synchronized*/MyThreadLocalScopeDate getThreadInstance(){
MyThreadLocalScopeDate instance =map.get();
if(instance==null){
instance = new MyThreadLocalScopeDate();
map.set(instance);
}
return instance;
} private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}

出处:https://www.cnblogs.com/pony1223/p/9251641.html

JAVA多线程学习五:线程范围内共享变量&ThreadLocal的更多相关文章

  1. JAVA多线程提高三:线程范围内共享变量&ThreadLocal

    今天我们学习的是如何在线程自己的范围内达到变量数据的共享,而各个线程之间又是互相独立开来,各自维护的,即我们说的ThreadLocal的作用. 一.概念 可以将每个线程用到的数据与对应的线程号存放到一 ...

  2. Java多线程学习篇——线程的开启

    随着开发项目中业务功能的增加,必然某些功能会涉及到线程以及并发编程的知识点.笔者就在现在的公司接触到了很多软硬件结合和socket通讯的项目了,很多的功能运用到了串口通讯编程,串口通讯编程的安卓端就是 ...

  3. JAVA多线程学习七-线程池

    为什么用线程池 1.创建/销毁线程伴随着系统开销,过于频繁的创建/销毁线程,会很大程度上影响处理效率 例如: 记创建线程消耗时间T1,执行任务消耗时间T2,销毁线程消耗时间T3 如果T1+T3> ...

  4. Java多线程学习之线程的取消与中断机制

    任务和线程的启动很容易.在大多数情况下我们都会让他们运行直到结束,或是让他们自行停止.但是,有时我们希望提前结束任务或是线程,可能是因为用户请求取消,或是线程在规定时间内没有结束,或是出现了一些问题迫 ...

  5. JAVA多线程学习十一-线程锁技术

    前面我们讲到了synchronized:那么这节就来将lock的功效. 一.locks相关类 锁相关的类都在包java.util.concurrent.locks下,有以下类和接口: |---Abst ...

  6. Java多线程学习总结--线程概述及创建线程的方式(1)

    在Java开发中,多线程是很常用的,用得好的话,可以提高程序的性能. 首先先来看一下线程和进程的区别: 1,一个应用程序就是一个进程,一个进程中有一个或多个线程.一个进程至少要有一个主线程.线程可以看 ...

  7. Java多线程学习之线程池源码详解

    0.使用线程池的必要性 在生产环境中,如果为每个任务分配一个线程,会造成许多问题: 线程生命周期的开销非常高.线程的创建和销毁都要付出代价.比如,线程的创建需要时间,延迟处理请求.如果请求的到达率非常 ...

  8. Java多线程学习(三)---线程的生命周期

    线程生命周期 摘要: 当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态.在线程的生命周期中,它要经过新建(New).就绪(Runnable).运行(Running).阻塞 ...

  9. Java多线程学习(二)---线程创建方式

    线程创建方式 摘要: 1. 通过继承Thread类来创建并启动多线程的方式 2. 通过实现Runnable接口来创建并启动线程的方式 3. 通过实现Callable接口来创建并启动线程的方式 4. 总 ...

随机推荐

  1. CONTRASTIVE REPRESENTATION DISTILLATION

    目录 概 主要内容 超参数的选择 代码 Tian Y., Krishnan D., Isola P. CONTRASTIVE REPRESENTATION DISTILLATION. arXiv pr ...

  2. vue为什么要设计成异步队列渲染

    异步队列渲染 上一篇文章是在vue2.0 中通过Object.defineProperty去拦截并监听数据变化的响应式原理,这篇文章将会沿着图谱继续深入探索,在依赖被通知变化了之后,会触发vue当中的 ...

  3. Java程序设计基础作业目录(作业笔记)

    持续更新中............. Java程序设计基础笔记 • [目录] 我的大学笔记>>> 第1章 初识Java>>> 1.1.4 学生成绩等级流程图练习 1 ...

  4. .NET 微服务——CI/CD(2):自动打包镜像

    准备工作 一.开启docker的tcp 我的服务器是linux,以端口2376为例,找到docker.service,在ExecStart下新增这段代码即可: -H tcp://0.0.0.0:237 ...

  5. [Atcoder Regular Contest 071 F & JZOJ5450]Neutral

    题目大意 一个无限长的序列\(a\), 需要满足 1.数列中的每一个数在\(1\)到\(n\)之间. 2.对于\(i>=n, j>=n\), \(a_i=a_j\). 3.对于\(i< ...

  6. shell2-if判断2

    1.条件判断if 判断条件:then //单分支语句 命令1 命令2fi 例子: #!/bin/bash ls if [ $? -eq 0 ]; then echo "执行成功了" ...

  7. post请求后获取不到请求头信息的原因

    在前台获取数据时,因为没有条件,所以不用传数据,用的post请求.再添加token验证时想着前端在请求时直接添加一个请求头信息就ok 没想到后台却获取不到请求头信息,打印了下日志发现是null,这是怎 ...

  8. Nginx日志配置及日志分析脚本案例

    https://blog.csdn.net/bbwangj/article/details/82186162 nginx的log日志分为access log 和 error log 其中access ...

  9. Linux的六种查找命令

    http://www.ruanyifeng.com/blog/2009/10/5_ways_to_search_for_files_using_the_terminal.html 1. find fi ...

  10. Oracle - 以 INSERT SQL语句形式导出结果集

    使用 SQLcl - 这是 SQL Developer 的命令行接口 下载 SQLcl sql sys/welcome@localhost:1521:orcl as sysdba #sql usern ...