今天我们学习的是如何在线程自己的范围内达到变量数据的共享,而各个线程之间又是互相独立开来,各自维护的,即我们说的ThreadLocal的作用。

一、概念

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

二、代码

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

  1. public class ThreadScopeShareData {
  2.  
  3. private static Map<Thread, Integer> threadData = new HashMap<Thread, Integer>();
  4. public static void main(String[] args) {
  5. for(int i=0;i<2;i++){
  6. new Thread(new Runnable(){
  7. @Override
  8. public void run() {
  9. int data = new Random().nextInt();
  10. System.out.println(Thread.currentThread().getName()
  11. + " has put data :" + data);
  12. threadData.put(Thread.currentThread(), data);
  13. new A().get();
  14. new B().get();
  15. }
  16. }).start();
  17. }
  18. }
  19.  
  20. static class A{
  21. public void get(){
  22. int data = threadData.get(Thread.currentThread());
  23. System.out.println("A from " + Thread.currentThread().getName()
  24. + " get data :" + data);
  25. }
  26. }
  27.  
  28. static class B{
  29. public void get(){
  30. int data = threadData.get(Thread.currentThread());
  31. System.out.println("B from " + Thread.currentThread().getName()
  32. + " get data :" + data);
  33. }
  34. }
  35. }

三、ThreadLocal

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

代码:

  1. public class ThreadLocalTest {
  2.  
  3. private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>();
  4. public static void main(String[] args) {
  5.  
  6. //启动两个线程
  7. for (int i = 0; i < 2; i++) {
  8. new Thread(new Runnable() {
  9. @Override
  10. public void run() {
  11. //创建每个线程私有的变量
  12. int data = new Random().nextInt(100);
  13. System.out.println(Thread.currentThread().getName()+" has put data: "+data);
  14. //往local里面设置值
  15. threadLocal.set(data);
  16. new A().get();
  17. new B().get();
  18. }
  19. }).start();
  20. }
  21. }
  22.  
  23. static class A{
  24. public void get(){
  25. int data =threadLocal.get();
  26. System.out.println("A from "+Thread.currentThread().getName()+" has get data: "+data);
  27. }
  28. }
  29.  
  30. static class B{
  31. public void get(){
  32. int data =threadLocal.get();
  33. System.out.println("B from "+Thread.currentThread().getName()+" has get data: "+data);
  34. }
  35. }
  36. }

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

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

  1. public class ThreadLocalTest {
  2.  
  3. private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>();
  4. public static void main(String[] args) {
  5.  
  6. //启动两个线程
  7. for (int i = 0; i < 2; i++) {
  8. new Thread(new Runnable() {
  9. @Override
  10. public void run() {
  11. //创建每个线程私有的变量
  12. int data = new Random().nextInt(100);
  13. System.out.println(Thread.currentThread().getName()+" has put data: "+data);
  14. //往local里面设置值
  15. threadLocal.set(data);
  16. //获取自己线程的MyThreadLocalScopeDate实例对象
  17. MyThreadLocalScopeDate myData = MyThreadLocalScopeDate.getThreadInstance();
  18. myData.setName("name"+data);
  19. myData.setAge(data);
  20. new A().get();
  21. new B().get();
  22. }
  23. }).start();
  24. }
  25. }
  26.  
  27. static class A{
  28. public void get(){
  29. int data =threadLocal.get();
  30. System.out.println("A from "+Thread.currentThread().getName()+" has get data: "+data);
  31. MyThreadLocalScopeDate myData = MyThreadLocalScopeDate.getThreadInstance();
  32. System.out.println("A from "+Thread.currentThread().getName()+" has get MyThreadLocalScopeDate name: "+myData.getName()+" , age: "+myData.getAge());
  33. }
  34. }
  35.  
  36. static class B{
  37. public void get(){
  38. int data =threadLocal.get();
  39. System.out.println("B from "+Thread.currentThread().getName()+" has get data: "+data);
  40. MyThreadLocalScopeDate myData = MyThreadLocalScopeDate.getThreadInstance();
  41. System.out.println("B from "+Thread.currentThread().getName()+" has get MyThreadLocalScopeDate name: "+myData.getName()+" , age: "+myData.getAge());
  42. }
  43. }
  44. }
  45.  
  46. class MyThreadLocalScopeDate{//单例模式
  47.  
  48. private MyThreadLocalScopeDate(){};//构造方法私有化
  49. private static ThreadLocal<MyThreadLocalScopeDate> map = new ThreadLocal<MyThreadLocalScopeDate>();//封装MyThreadLocalScopeDate是线程实现范围内共享
  50.  
  51. //思考AB两个线程过来的情况 自己分析 AB都需要的自己的对象 没有关系 所以不需要同步 如果有关系就需要同步了
  52. public static /*synchronized*/MyThreadLocalScopeDate getThreadInstance(){
  53. MyThreadLocalScopeDate instance =map.get();
  54. if(instance==null){
  55. instance = new MyThreadLocalScopeDate();
  56. map.set(instance);
  57. }
  58. return instance;
  59. }
  60.  
  61. private String name;
  62. private int age;
  63. public String getName() {
  64. return name;
  65. }
  66. public void setName(String name) {
  67. this.name = name;
  68. }
  69. public int getAge() {
  70. return age;
  71. }
  72. public void setAge(int age) {
  73. this.age = age;
  74. }
  75. }

参考资料:

《多线程视频》张孝祥

JAVA多线程提高三:线程范围内共享变量&ThreadLocal的更多相关文章

  1. JAVA多线程学习五:线程范围内共享变量&ThreadLocal

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

  2. Java多线程——线程范围内共享变量

    多个线程访问共享对象和数据的方式 1.如果每个线程执行的代码相同,可以使用同一个Runnable对象,这个Runnable对象中有那个共享数据,例如,买票系统就可以这么做. package java_ ...

  3. Java多线程——线程范围内共享变量和ThreadLocal

    多个线程访问共享对象和数据的方式 1.如果每个线程执行的代码相同,可以使用同一个Runnable对象,这个Runnable对象中有那个共享数据,例如,买票系统就可以这么做. package java_ ...

  4. JAVA 并发编程-线程范围内共享变量(五)

    线程范围内共享变量要实现的效果为: 多个对象间共享同一线程内的变量 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsi ...

  5. -1-5 java 多线程 概念 进程 线程区别联系 java创建线程方式 线程组 线程池概念 线程安全 同步 同步代码块 Lock锁 sleep()和wait()方法的区别 为什么wait(),notify(),notifyAll()等方法都定义在Object类中

     本文关键词: java 多线程 概念 进程 线程区别联系 java创建线程方式 线程组 线程池概念 线程安全 同步 同步代码块 Lock锁  sleep()和wait()方法的区别 为什么wait( ...

  6. Java多线程——进程和线程

    Java多线程——进程和线程 摘要:本文主要解释在Java这门编程语言中,什么是进程,什么是线程,以及二者之间的关系. 部分内容来自以下博客: https://www.cnblogs.com/dolp ...

  7. Java多线程系列--“JUC线程池”06之 Callable和Future

    概要 本章介绍线程池中的Callable和Future.Callable 和 Future 简介示例和源码分析(基于JDK1.7.0_40) 转载请注明出处:http://www.cnblogs.co ...

  8. Java多线程系列--“JUC线程池”02之 线程池原理(一)

    概要 在上一章"Java多线程系列--“JUC线程池”01之 线程池架构"中,我们了解了线程池的架构.线程池的实现类是ThreadPoolExecutor类.本章,我们通过分析Th ...

  9. Java多线程系列--“JUC线程池”03之 线程池原理(二)

    概要 在前面一章"Java多线程系列--“JUC线程池”02之 线程池原理(一)"中介绍了线程池的数据结构,本章会通过分析线程池的源码,对线程池进行说明.内容包括:线程池示例参考代 ...

随机推荐

  1. Python学习 - 入门篇2(更新中)

    前言 学习渠道:慕课网:Python进阶 记录原因:我只是想边上课边做笔记而已,呵呵哒 食用提示:教程环境基于Python 2.x,有些内容在Python 3.x中已经改变 函数式编程 定义:一种抽象 ...

  2. 《我是一只it小小鸟》观后感

    在这个学期开始的时候我们的老师推荐给我们这本书.在很多的网站上只要一提到IT,总会有人推荐这本书,我在读这本书之前看了很多关于它的书评,其中有一位网友的一句话让我对它产生了很大的兴趣:“印象最深的是书 ...

  3. bash编程2

    bash基础编程 前言:条件测试语法有两种书写模式,一种时[expression] ,另外一种是[[exprssion]] ,为了在书写条件测试的过程中,不让大家将两种格式互相混淆,那么在这里只讲一种 ...

  4. larave5.6 引入自定义函数库时,报错不能重复定义

    方法一:使用function_exists判断 方法二:使用命名空间 namespace test; function test(){ echo 'test/test'; } namespace te ...

  5. oracle 关于表数据delete 后如何恢复

    今天在PL/SQL中操作不小心删掉了某个表的部分数据,这可吓坏了本猿:于是悄悄的打开电脑,赶紧找度娘帮忙.经过度娘的小爬虫帮助,几分钟就把数据恢复了. 那么表数据delete掉后怎么恢复呢? 用fla ...

  6. mysql 8 server windows 安装经验分享

    windows下安装一般分为文件/msi安装文件 本章我们说的是文件行的mysql server 安装 下载地址:https://dev.mysql.com/downloads/mysql/ 下载完后 ...

  7. C# WebBrowser控件模拟登录

    一.问题点: 1.模拟登录后,如果带有嵌套的iframe嵌套,不好读取iframe内容,可以直接指定iframe抓取网址 2.C# 清除WebBrowser控件的Session和Cookie 参考文档 ...

  8. 第156天:canvas(三)

    一.变形 1.1 translate translate(x, y) ​ 用来移动 canvas 的原点到指定的位置 ​ translate方法接受两个参数.x 是左右偏移量,y 是上下偏移量,如右图 ...

  9. 【数据库_Postgresql】sql语句添加序号,timestamp格式时间截取日期和时间

    SELECT ROW_NUMBER() OVER (ORDER BY sr.receiptid ASC) AS 序号, sr.receiptid, sr.receiptdate, DATE(sr.re ...

  10. [AT2172] [agc007_e] Shik and Travel

    题目链接 AtCoder:https://agc007.contest.atcoder.jp/tasks/agc007_e 洛谷:https://www.luogu.org/problemnew/sh ...