基本 ThreadLocal 用法

Java ThreadLocal 用于创建线程局部变量。众所周知,Object 的线程共享它的变量,因此该变量不是线程安全的。我们可以使用同步来保证线程安全,但是如果我们想避免同步,ThreadLocal 允许我们创建线程本地的变量,即只有那个线程可以读取或写入那些变量,所以其他线程执行同一段代码将无法访问彼此的 ThreadLocal 变量。

这可以用来使用 ThreadLocal 变量。在你有一个线程池的情况下,例如在 Web 服务中。例如,每次为每个请求创建一个 SimpleDateFormat 对象都非常耗时,并且由于 SimpleDateFormat 不是线程安全的,因此无法创建静态对象,因此我们可以创建一个 ThreadLocal,以便我们可以执行线程安全操作,而无需每次创建 SimpleDateFormat 的开销时间。

下面的代码显示了如何使用它:

每个线程都有自己的 ThreadLocal 变量,他们可以使用 get()set() 方法来获取默认值或将其值更改为 Thread 的本地值。

ThreadLocal 实例通常是希望将状态与线程相关联的类中的私有静态字段。

这是一个小例子,展示了在 java 程序中使用 ThreadLocal 并证明每个线程都有自己的 ThreadLocal 变量副本。

package com.examples.threads;

import java.text.SimpleDateFormat;
import java.util.Random;

public class ThreadLocalExample implements Runnable{

    // SimpleDateFormat is not thread-safe, so give one to each thread
 // SimpleDateFormat is not thread-safe, so give one to each thread
    private static final ThreadLocal<SimpleDateFormat> formatter = new ThreadLocal<SimpleDateFormat>(){
        @Override
        protected SimpleDateFormat initialValue()
        {
            return new SimpleDateFormat("yyyyMMdd HHmm");
        }
    };
    
    public static void main(String[] args) throws InterruptedException {
        ThreadLocalExample obj = new ThreadLocalExample();
        for(int i=0 ; i<10; i++){
            Thread t = new Thread(obj, ""+i);
            Thread.sleep(new Random().nextInt(1000));
            t.start();
        }
    }

    @Override
    public void run() {
        System.out.println("Thread Name= "+Thread.currentThread().getName()+" default Formatter = "+formatter.get().toPattern());
        try {
            Thread.sleep(new Random().nextInt(1000));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        formatter.set(new SimpleDateFormat());
        
        System.out.println("Thread Name= "+Thread.currentThread().getName()+" formatter = "+formatter.get().toPattern());
    }

}

输出:

Thread Name= 0 default Formatter = yyyyMMdd HHmm

Thread Name= 1 default Formatter = yyyyMMdd HHmm

Thread Name= 0 formatter = M/d/yy h:mm a

Thread Name= 2 default Formatter = yyyyMMdd HHmm

Thread Name= 1 formatter = M/d/yy h:mm a

Thread Name= 3 default Formatter = yyyyMMdd HHmm

Thread Name= 4 default Formatter = yyyyMMdd HHmm

Thread Name= 4 formatter = M/d/yy h:mm a

Thread Name= 5 default Formatter = yyyyMMdd HHmm

Thread Name= 2 formatter = M/d/yy h:mm a

Thread Name= 3 formatter = M/d/yy h:mm a

Thread Name= 6 default Formatter = yyyyMMdd HHmm

Thread Name= 5 formatter = M/d/yy h:mm a

Thread Name= 6 formatter = M/d/yy h:mm a

Thread Name= 7 default Formatter = yyyyMMdd HHmm

Thread Name= 8 default Formatter = yyyyMMdd HHmm

Thread Name= 8 formatter = M/d/yy h:mm a

Thread Name= 7 formatter = M/d/yy h:mm a

Thread Name= 9 default Formatter = yyyyMMdd HHmm

Thread Name= 9 formatter = M/d/yy h:mm a

正如我们从输出中看到的那样,Thread-0 已经改变了 formatter 的值,但仍然是 thread-2 默认格式化程序与初始化值相同。