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;

         }



}

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值