6.22 Java多线程ThreadLocal

6.22 Java多线程ThreadLocal

线程变量的特点

  • 多线程环境下,每个线程都有自己的数据

  • 一个线程的局部变量只有自己能看见,不会影响其他线程(使用局部变量比使用其他变量好)

ThreadLocal的特点

  • ThreadLocal能够放一个线程级别的变量

  • 本身能够被多个线程共享使用,又能达到线程安全的目的

  • 在多线程环境下保证成员变量的安全

常用方法:

get/set/initialValue(初始化)方法

JDK建议ThreadLocal定义为private static

ThreadLocal常用的地方(内部存储结构类似于Map)

  • 为每一个线程绑定一个数据库连接(每一个线程有一个数据库连接)、HTTP请求、用户身份信息等

    • Hibernate的Session 工具类HibernateUtil

    • 通过不同的线程对象设置Bean属性,保证各个线程Bean对象的独立性

  • 每个线程拥有一个ThreadLocal

    • ThreadLocal内部的存储结构类似于Map

      • key是线程信息

      • value是对应的存储内容

      • 这样可以达到每个线程的数据相互独立,又可以共享一块大的区域。达到在多线程环境下保证成员变量的安全。

ThreadLocal的实例demo
package thread.rearrangement;

/**
* ThreadLocal:每个线程自身的存储区域(局部)
* get/set/initialValue
* @since JDK 1.8
* @date 2021/6/22
* @author Lucifer
*/
public class ThreadLocalTestNo1 {

   /*声明类属性*/
//   private static ThreadLocal<Integer> threadLocal = new ThreadLocal<>();

   /*更改初始值--->创建threadlocal的子类,重写initialValue*/
//   private static ThreadLocal<Integer> threadLocal = new ThreadLocal<>(){
//       protected Integer initialValue(){
//           return 200; //初始化的值就更改成了200
//       };
//   };

   /*lambda表达式更改初始值*/
//   private static ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(() -> {
//       return 200;
//   });

   /*更加简化代码*/
   private static ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(() -> 200);

   /*
   这样就开辟了一个大的存储空间
   每来一个线程都会自动开辟一个区域
   这些开辟的空间都会在大的存储空间内
    */

   public static void main(String[] args) {
       System.out.println(Thread.currentThread().getName() + "--->" + threadLocal.get());
       /*因为是整数,所以默认值是Null,不是零*/

       /*设置值*/
       threadLocal.set(99);
       System.out.println(Thread.currentThread().getName() + "--->" + threadLocal.get());

       /*
       现在所有的存储区域属于主线程main
        */

       /*实例化线程内部类*/
       new Thread(new MyRun()).start();
  }

   /**
    * 写一个内部类,实现Runnable接口
    * 线程类
    */
   public static class MyRun implements Runnable{

       /*重写run方法*/
       @Override
       public void run(){

           /*设置个随机数值*/
           threadLocal.set((int)(Math.random() * 99));

           /*获取值*/
           System.out.println(Thread.currentThread().getName() + "--->" + threadLocal.get());

      }
  }
}
/*
每一个线程拥有自己的一个大的空间,里面存储的是自己的数据,大家相互不影响
*/
ThreadLocal修改属性变量的实例demo
package thread.rearrangement;

/**
* 每个线程存储自己的数据,更改不会影响其他线程
* @since JDK 1.8
* @date 2021/6/23
* @author Lucifer
*/
public class ThreadLocalTestNo2 {

   /*更加简化代码*/
   private static ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(() -> 1);

   /*
   这样就开辟了一个大的存储空间
   每来一个线程都会自动开辟一个区域
   这些开辟的空间都会在大的存储空间内
    */

   public static void main(String[] args) {
       for (int i=0; i<5; i++){
           new Thread(new MyRun()).start();
      }
  }

   /**
    * 写一个内部类,实现Runnable接口
    * 线程类
    */
   public static class MyRun implements Runnable{

       /*重写run方法*/
       @Override
       public void run(){

           /*获取当前线程的值*/
           Integer left = threadLocal.get();
           /*
           get方法的作用:
           返回当前线程的此线程局部变量的副本中的值
            */

           /*获取值*/
           System.out.println(Thread.currentThread().getName() + "得到了--->" + left);

           /*操作资源数*/
           threadLocal.set(left-1);

           /*再次打印*/
           System.out.println(Thread.currentThread().getName() + "还剩下--->" + threadLocal.get());

      }
  }
}
ThreadLocal调用分析
package thread.rearrangement;

/**
* 分析ThreadLocal环境
* @since JDk 1.8
* @date 2021/6/23
* @author Lucifer
*/
public class ThreadLocalTestNo3 {

   /*创建ThreadLocal存储空间*/
   private static ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(() -> 1);

   public static void main(String[] args) {
       new Thread(new MyRun()).start();
       new Thread(new MyRun()).start();
  }

   /**
    * 创建一个内部线程类
    */
   public static class MyRun implements Runnable{

       /*写一个构造器*/
       public MyRun(){
           threadLocal.set(-100);
           System.out.println(Thread.currentThread().getName() + "--->" + threadLocal.get());
      }

       /*重写Runnable接口的run方法*/
       @Override
       public void run(){
           System.out.println(Thread.currentThread().getName() + "--->" + threadLocal.get());
      }
  }
}
InheritableThreadLocal拷贝线程数据实例
package thread.rearrangement;

/**
* InheritableThreadLocal:继承上下文、环境的数据、起点
* @since JDK 1.8
* @date 2021/6/23
* @author Lucifer
*/
public class ThreadLocalTestNo4 {

   /*开辟ThreadLocal线程空间*/
   private static InheritableThreadLocal<Integer> threadLocal = new InheritableThreadLocal<>();

   public static void main(String[] args) {

       /*设置值*/
       threadLocal.set(2);

       /*线程属性变量*/
       System.out.println(Thread.currentThread().getName() + "--->" + threadLocal.get());

       /*修改拷贝的属性*/
       threadLocal.set(200);
       /*
       这样修改以后上面的线程就是初始设置的2
       下面的新的线程为现在设置的200
        */

       /*新建一个线程体*/
       new Thread(() -> {
           System.out.println(Thread.currentThread().getName() + "--->" + threadLocal.get());
      }).start();
       /*
       这是新的线程体的,他的值是null
       如果将ThreadLocal改为InheritableThreadLocal那么set值就会被延续到下一个线程体当中
       因为新建的线程由main线程开辟,所以会将数据拷贝一份给子线程
       这是拷贝不是共享,所以新的线程体还可以对属性进行修改
        */
  }
}

 

posted @ 2021-06-23 10:18  俊king  阅读(137)  评论(0)    收藏  举报