【无标题】

java.lang.ThreadLocal类详解

文章目录


前言

java.lang.ThreadLocal 类提供线程局部变量,所以ThreadLocal并不是一个Thread,而是Thread的局部变量。很多公司都注重使用java.lang.ThreadLocal 类,因为ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。它通过为每个线程提供一个独立的变量副本解决了变量并发访问的冲突问题。在很多情况下,ThreadLocal比直接使用synchronized同步机制解决线程安全问题更简单,更方便,且结果程序拥有更高的并发性。所以使用这个工具类可以很简洁地编写出优美的多线程程序。


一、基础概念

类声明

public class ThreadLocal<T> extends Object

类方法

【无标题】
1.java.lang.ThreadLocal.get() 方法返回此线程局部变量的当前线程副本中的值。

import java.lang.*;

public class ThreadLocalDemo {

   public static void main(String[] args) {

     ThreadLocal<Integer> tlocal = new ThreadLocal<Integer>();  

     tlocal.set(100);
     // returns the current thread's value
     System.out.println("value = " + tlocal.get());
 
     tlocal.set(90);
     // returns the current thread's value of 
     System.out.println("value = " + tlocal.get());
   }
} 

2 .java.lang.ThreadLocal.initialValue() 方法返回此线程当前线程局部变量的初始值。

import java.lang.*;

public class ThreadLocalDemo {

   public static void main (String [] args) {

      newThread t1 = new newThread("R");
      newThread t2 = new newThread("S");

      // this will call run() method
      t1.start();
      t2.start();
   }
}

class newThread extends Thread
{
   private static ThreadLocal t =
      new ThreadLocal()
          {
             protected Object initialValue() {
                return new Integer(n--);
             }
          };

   private static int n = 10;
   newThread(String name) {
      super(name);
   }

   public void run() {
      for(int i = 0; i < 2; i++)
           System.out.println (getName() + " " + t.get());
   }
} 

3 .java.lang.ThreadLocal.remove() 方法删除该线程当前线程局部变量的值。

import java.lang.*;

public class ThreadLocalDemo {

   public static void main(String[] args) {

     ThreadLocal<Integer> tlocal = new ThreadLocal<Integer>();  

     /* sets the current thread's copy of this thread-local variable
     to the specified value. */

     tlocal.set(50);
     // returns the current thread's value of this thread-local
     System.out.println("value = " + tlocal.get());
 
     // removes the current thread's value 
     tlocal.remove();
     // returns the current thread's value of this thread-local
     System.out.println("value = " + tlocal.get());
   }
}

4 .java.lang.ThreadLocal.set() 方法设置此线程局部变量的当前线程副本中指定的值。

import java.lang.*;

public class ThreadLocalDemo {

   public static void main(String[] args) {

     ThreadLocal<Integer> tlocal = new ThreadLocal<Integer>();  

     /* sets the current thread's copy of this thread-local variable
     to the specified value. */

     tlocal.set(100);
     // returns the current thread's value of this thread-local
     System.out.println("value = " + tlocal.get());
 
     tlocal.set(0);
     // returns the current thread's value of this thread-local
     System.out.println("value = " + tlocal.get());
   }
} 

类构造函数

【无标题】

二、使用案例

public class ThreadLocalExsample {
​
 /**
 * 创建了一个MyRunnable实例,并将该实例作为参数传递给两个线程。两个线程分别执行run()方法,
 * 并且都在ThreadLocal实例上保存了不同的值。如果它们访问的不是ThreadLocal对象并且调用的set()方法被同步了,
 * 则第二个线程会覆盖掉第一个线程设置的值。但是,由于它们访问的是一个ThreadLocal对象,
 * 因此这两个线程都无法看到对方保存的值。也就是说,它们存取的是两个不同的值。
 */
    public static class MyRunnable implements Runnable {
 /**
 * 例化了一个ThreadLocal对象。我们只需要实例化对象一次,并且也不需要知道它是被哪个线程实例化。
 * 虽然所有的线程都能访问到这个ThreadLocal实例,但是每个线程却只能访问到自己通过调用ThreadLocal的
 * set()方法设置的值。即使是两个不同的线程在同一个ThreadLocal对象上设置了不同的值,
 * 他们仍然无法访问到对方的值。
 */
      private ThreadLocal threadLocal = new ThreadLocal();
      @Override
      public void run() {
         //一旦创建了一个ThreadLocal变量,你可以通过如下代码设置某个需要保存的值
         threadLocal.set((int) (Math.random() * 100D));
         try {
               Thread.sleep(2000);
         } catch (InterruptedException e) {
         }
          //可以通过下面方法读取保存在ThreadLocal变量中的值
        System.out.println("-------threadLocal value-------"+threadLocal.get());
     }
   }
​
   public static void main(String[] args) {
      MyRunnable sharedRunnableInstance = new MyRunnable();
      Thread thread1 = new Thread(sharedRunnableInstance);
      Thread thread2 = new Thread(sharedRunnableInstance);
      thread1.start();
      thread2.start();
   }
}

运行结果
-------threadLocal value-------38
-------threadLocal value-------88
ThreadLocal 中 set 和 get 操作的都是对应线程的 table数组,因此在不同的线程中访问同一个 ThreadLocal 对象的 set 和 get 进行存取数据是不会相互干扰的。


总结

在每个线程Thread内部有一个ThreadLocal.ThreadLocalMap类型的成员变量threadLocals,这个threadLocals就是用来存储实际的变量副本的,键值为当前ThreadLocal变量,value为变量副本(即T类型的变量)。 初始时,在Thread里面,threadLocals为空,当通过ThreadLocal变量调用get()方法或者set()方法,就会对Thread类中的threadLocals进行初始化,并且以当前ThreadLocal变量为键值,以ThreadLocal要保存的副本变量为value,存到threadLocals。 然后在当前线程里面,如果要使用副本变量,就可以通过get方法在threadLocals里面查找。

上一篇:ThreadLocal 应用及原理、弱引用介绍、同线程内数据共享


下一篇:浅谈threadlocal