当前位置: 首页 > news >正文

ThreadLocal

一、概述

ThreadLocal是lang包下的一个与线程有关的类。该类提供了线程局部(thread-local)变量。这些变量不同于它们的普通对应物,因为访问某个变量(通过其get或set方法)的每个线程都有自己的局部变量,它独立于变量的初始化副本。ThreadLocal实例通常是类中的private static字段,它们希望将状态与某一个线程(例如,用户ID或者事务ID)相关联。

每个线程都保持对其线程局部变量副本的隐式引用,只要线程是活动的并且ThreadLocal实例是可访问的。在线程消失后,其线程局部实例的所有副本都会垃圾回收。

ThreadLocal类,可以用来同线程内共享数据而不与其他线程共享数据。有点解释不清楚这个意思。比如,某个任务中,产生一个局部变量,将局部变量传给两个实例对象,实例对象使用这个变量。如果在这个任务中,开启两条或多条线程,如果不使用ThreadLocal或其他方式,那么,每个线程中得到的这个局部变量不是本线程独有的,而是所有线程都共有。

二、示例

1. 不使用ThreadLocal或其他方式,多个线程间数据不能隔离导致的数据污染

/**

 * 测试线程间数据污染

 *

 * @author Administrator

 *

 */

public class ThreadSharedDataDirty {



         // 线程共享的数据

         private static int data = 0;



         public static void main(String[] args) {

                   // 循环开启两条线程

                   for (int i = 0; i < 2; i++) {

                            new Thread(new Runnable() {

                                     // 业务逻辑,获取随机数,并且调用打印类的打印方法

                                     public void run() {

                                               data = new Random().nextInt();

                                               // 在线程中打印

                                               System.out.println(Thread.currentThread().getName() + ":" + data);

                                               // 分别调用A类和B类的打印业务

                                               new A().get();

                                               new B().get();

                                     }

                            }).start();

                   }

         }



         // 从线程中获取数据,打印的A类

         static class A {

                   public void get() {

                            System.out.println("A " + Thread.currentThread().getName() + ":" + data);

                   }

         }



         // 从线程中获取数据,打印的B类

         static class B {

                   public void get() {

                            System.out.println("B " + Thread.currentThread().getName() + ":" + data);

                   }

         }

}

2. 使用以线程ID或者线程名字为键以本线程局部变量值为值的map保存线程布局变量列表,避免线程间数据污染

/**

 * 使用线程局部变量列表避免线程间共享数据污染

 *

 * @author Administrator

 *

 */

public class ThreadSharedDataIsolate {



         // 线程共享的数据

         private static int data = 0;



         // 线程局部变量与线程的对照存储列表

         private static Map<Long, Integer> threadLocalMap = new HashMap<Long, Integer>();



         public static void main(String[] args) {

                   // 循环开启两条线程

                   for (int i = 0; i < 2; i++) {

                            new Thread(new Runnable() {

                                     // 业务逻辑,获取随机数,并且调用打印类的打印方法

                                     public void run() {

                                               // 为了避免布局变量在存入线程布局变量列表时被污染,将获取随机数据和存放数据的业务同步

                                               // 如果扩大同步的范围,即使不使用线程布局变量列表也可以避免线程污染

                                               // 扩大同步的特点是,1,效率下降;2,这里主要是为展示ThreadLocal类做铺垫,所以,没有使用扩大同步

                                               synchronized (threadLocalMap) {

                                                        // 获取随机数

                                                        data = new Random().nextInt();

                                                        // 将局部变量存放到线程局部变量列表

                                                        threadLocalMap.put(Thread.currentThread().getId(), data);

                                               }

                                               System.out.println(Thread.currentThread().getId());

                                               // 在线程中打印

                                               System.out.println(Thread.currentThread().getName() + ":"

                                                                 + threadLocalMap.get(Thread.currentThread().getId()));



                                               // 分别调用A类和B类的打印业务

                                               new A().get();

                                               new B().get();

                                     }

                            }).start();

                   }

         }



         // 从线程中获取数据,打印的A类

         static class A {

                   public void get() {

                            // 从线程布局变量列表中取出本线程的局部变量

                            int data = threadLocalMap.get(Thread.currentThread().getId());

                            System.out.println("A " + Thread.currentThread().getName() + ":" + data);

                   }

         }



         // 从线程中获取数据,打印的B类

         static class B {

                   public void get() {

                            // 从线程布局变量列表中取出本线程的局部变量

                            int data = threadLocalMap.get(Thread.currentThread().getId());

                            System.out.println("B " + Thread.currentThread().getName() + ":" + data);

                   }

         }

}

3. 使用ThreadLocal类存放简单数据类型时,避免线程共享数据污染问题

/**

 * 测试ThreadLocl类存放简单数据类型

 *

 * @author Administrator

 *

 */

public class ThreadLocalSimpleDataTest {



         // 创建一个ThreadLocal对象

         private static ThreadLocal<Integer> threadLocal = new ThreadLocal<>();



         public static void main(String[] args) {

                   // 循环开启两条线程

                   for (int i = 0; i < 2; i++) {

                            new Thread(new Runnable() {

                                     // 业务逻辑,获取随机数,并且调用打印类的打印方法

                                     public void run() {

                                               int random = new Random().nextInt();

                                               // 将随机数放到ThreadLocal中

                                               threadLocal.set(random);

                                               // 在线程中打印

                                               System.out.println(Thread.currentThread().getName() + ":" + threadLocal.get());

                                               // 分别调用A类和B类的打印业务

                                               A.get();

                                               B.get();

                                     }

                            }).start();

                   }

         }



         // 从线程中获取数据,打印的A类

         static class A {

                   public static void get() {

                            int a = threadLocal.get();

                            System.out.println("A " + Thread.currentThread().getName() + ":" + a);

                   }

         }



         // 从线程中获取数据,打印B类

         static class B {

                   public static void get() {

                            int a = threadLocal.get();

                            System.out.println("B " + Thread.currentThread().getName() + ":" + a);

                   }

         }

}

4. 使用ThreadLocal类存放引用数据类型时,避免线程间共享数据污染

使用引用数据类型可以存放多个变量供线程共享。

/**

 * 测试ThreadLocl类存放引用数据类型

 *

 * @author Administrator

 *

 */

public class ThreadLocalReferenceDataTest {



         // 创建一个ThreadLocal对象

         private static ThreadLocal<ThreadData> threadLocal = new ThreadLocal<>();



         public static void main(String[] args) {

                   // 循环开启两条线程

                   for (int i = 0; i < 2; i++) {

                            new Thread(new Runnable() {

                                     // 业务逻辑,获取随机数,并且调用打印类的打印方法

                                     public void run() {

                                               int random = new Random().nextInt();

                                               // 创建线程数据类

                                               ThreadData data = new ThreadData();

                                               data.setAge(random);

                                               data.setName("name_" + random);

                                               // 将线程数据类放到ThreadLocal中

                                               threadLocal.set(data);

                                               // 在线程中打印

                                               System.out.println(Thread.currentThread().getName() + ":" + threadLocal.get().getAge());

                                               // 分别调用A类和B类的打印业务

                                               A.get();

                                               B.get();

                                     }

                            }).start();

                   }

         }



         // 从线程中获取数据,打印的A类

         static class A {

                   public static void get() {

                            ThreadData data = threadLocal.get();

                            System.out.println(

                                               "A " + Thread.currentThread().getName() + " name : " + data.getName() + "; age : " + data.getAge());

                   }

         }



         // 从线程中获取数据,打印B类

         static class B {

                   public static void get() {

                            ThreadData data = threadLocal.get();

                            System.out.println(

                                               "B " + Thread.currentThread().getName() + " name : " + data.getName() + "; age : " + data.getAge());

                   }

         }

}



// 创建一个包含两个属性的类

class ThreadData {

         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;

         }



}

5. 将ThreadLocal封装到引用类中,通过类似饿汉式模式获得引用类的实例,避免线程间共享数据污染

认为是对上面一种方式的升级版,线程内单例,仿照饿汉式单例模式实现。

/**

 * 测试ThreadLocl类存放引用数据类型的升级版,使用线程内单例模式

 *

 * @author Administrator

 *

 */

public class ThreadLocalReferenceDataTest2 {



         public static void main(String[] args) {

                   // 循环开启两条线程

                   for (int i = 0; i < 2; i++) {

                            new Thread(new Runnable() {

                                     // 业务逻辑,获取随机数,并且调用打印类的打印方法

                                     public void run() {

                                               int random = new Random().nextInt();

                                               // 获取数据类

                                               ThreadDataHungary data = ThreadDataHungary.getThreadInstance();

                                               data.setAge(random);

                                               data.setName("name_" + random);

                                               // 在线程中打印

                                               System.out.println(

                                                                 Thread.currentThread().getName() + ":" + ThreadDataHungary.getThreadInstance().getAge());

                                               // 分别调用A类和B类的打印业务

                                               A.get();

                                               B.get();

                                     }

                            }).start();

                   }

         }



         // 从线程中获取数据,打印的A类

         static class A {

                   public static void get() {

                            ThreadDataHungary data = ThreadDataHungary.getThreadInstance();

                            System.out.println(

                                               "A " + Thread.currentThread().getName() + " name : " + data.getName() + "; age : " + data.getAge());

                   }

         }



         // 从线程中获取数据,打印B类

         static class B {

                   public static void get() {

                            ThreadDataHungary data = ThreadDataHungary.getThreadInstance();

                            System.out.println(

                                               "B " + Thread.currentThread().getName() + " name : " + data.getName() + "; age : " + data.getAge());

                   }

         }

}



// 创建一个包含两个属性的类

class ThreadDataHungary {

         // 将构造器私有化

         private ThreadDataHungary() {

         }



         // 对外提供静态的方式获取实例

         public static ThreadDataHungary getThreadInstance() {

                   // 从线程局部变量中获取实例

                   instance = threadLocal.get();

                   // 如果实例为空,则创建一个实例,并放入线程局部变量

                   if (null == instance) {

                            // 创建一个实例

                            instance = new ThreadDataHungary();

                            threadLocal.set(instance);

                   }

                   // 返回实例

                   return instance;

         }



         // 创建一个ThreadLocal类来存放实例

         private static ThreadLocal<ThreadDataHungary> threadLocal = new ThreadLocal<ThreadDataHungary>();

         // 创建一个null的实例

         private static ThreadDataHungary instance = null;

         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;

         }



}

 

相关文章:

  • [one_demo_18]js定时器的示例
  • Java8部分新特性
  • jvm简介
  • mybatis使用foreach处理List中的Map
  • log4j2的配置文件
  • 一个用java的NIO实现的socket的客户端和服务端的demo
  • 使用java的nio的pipe实现两个线程间传送数据的demo
  • org.hibernate.TransactionException: nested transactions not supported异常
  • elasticsearch
  • rancher简介
  • InfluxDB+cAdvisor+Grafana容器管理
  • serviceComb[No schema defined for start.servicecomb.io:]异常
  • ServiceComb
  • kubernetes
  • 生产报redis连接满的问题
  • 《网管员必读——网络组建》(第2版)电子课件下载
  • gcc介绍及安装
  • Java应用性能调优
  • JDK 6和JDK 7中的substring()方法
  • vue-cli3搭建项目
  • 基于Android乐音识别(2)
  • 京东美团研发面经
  • 通过几道题目学习二叉搜索树
  • 白色的风信子
  • 从如何停掉 Promise 链说起
  • 交换综合实验一
  • ​Spring Boot 分片上传文件
  • # 深度解析 Socket 与 WebSocket:原理、区别与应用
  • (+4)2.2UML建模图
  • (4)(4.6) Triducer
  • (6)设计一个TimeMap
  • (八)光盘的挂载与解挂、挂载CentOS镜像、rpm安装软件详细学习笔记
  • (大众金融)SQL server面试题(1)-总销售量最少的3个型号的车及其总销售量
  • (每日持续更新)信息系统项目管理(第四版)(高级项目管理)考试重点整理 第13章 项目资源管理(七)
  • (五)大数据实战——使用模板虚拟机实现hadoop集群虚拟机克隆及网络相关配置
  • (学习日记)2024.01.19
  • (一)kafka实战——kafka源码编译启动
  • (转)Scala的“=”符号简介
  • (转)真正的中国天气api接口xml,json(求加精) ...
  • **PHP二维数组遍历时同时赋值
  • 、写入Shellcode到注册表上线
  • .NET 中选择合适的文件打开模式(CreateNew, Create, Open, OpenOrCreate, Truncate, Append)
  • [ vulhub漏洞复现篇 ] Celery <4.0 Redis未授权访问+Pickle反序列化利用
  • [.NET]桃源网络硬盘 v7.4
  • [android] 切换界面的通用处理
  • [Apio2012]dispatching 左偏树
  • [bzoj1901]: Zju2112 Dynamic Rankings
  • [bzoj4010][HNOI2015]菜肴制作_贪心_拓扑排序
  • [C++核心编程](四):类和对象——封装
  • [Docker]六.Docker自动部署nodejs以及golang项目
  • [EFI]ASUS EX-B365M-V5 Gold G5400 CPU电脑 Hackintosh 黑苹果引导文件
  • [ExtJS5学习笔记]第三十节 sencha extjs 5表格gridpanel分组汇总
  • [Gamma]阶段测试报告
  • [Hadoop in China 2011] 蒋建平:探秘基于Hadoop的华为共有云
  • [MYSQL数据库]- 索引