Java ThreadLocal【译】

Java的ThreadLocal类使你可以创建一个只能被一个线程读取和写入的变量。因此,即使两个线程在执行相同的代码,并且引用了同一个ThreadLocal变量,两个线程也互相无法获取到对方的ThreadLocal变量。因此,Java的ThreadLocal类提供了一个简单的方法来实现代码的线程安全。

创建一个ThreadLocal

创建一个ThreadLocal实例和创建一个普通的Java对象相同 — 通过new操作。以下是一个创建一个ThreadLocal变量的示例:

private ThreadLocal threadLocal = new ThreadLocal();

set ThreadLocal的值

一旦ThreadLocal被创建,你就可以通过set()方法将值保存在其中。

threadLocal.set("A thread local value");

get ThreadLocal的值

你可以通过get()方法来读取存储在其中的值。以下是一个获取Java ThreadLocal中的值的示例:

String threadLocalValue = (String) threadLocal.get();

移除ThreadLocal值

可以移除一个ThreadLocal中的变量集。你可以通过调用ThreadLocal.remove()方法来移除一个值。以下是移除一个Java ThreadLocal中数据集的示例:

threadLocal.remove();

通用ThreadLocal

你可以通过泛型来创建一个ThreadLocal,使用泛型后,只有指定类型的值可以被set到ThreadLocal。另外,你不需要对get()方法返回的值进行强转。以下是一个泛型ThreadLocal的示例:

private ThreadLocal<String> myThreadLocal = new ThreadLocal<String>();
myThreadLocal.set("Hello ThreadLocal");

String threadLocalValue = myThreadLocal.get();

初始化ThreadLocal的值

可以为Java ThreadLocal设置一个初始化值,这个值是在调用set()方法前第一次调用get()方法时被使用到的一个新值。你有两个选项用来为一个ThreadLocal来指定一个初始化值:

  • 通过使用Supplier接口实现来创建ThreadLocal

重写initiaValue()方法

第一种给Java ThreadLocal指定初始值的方法是创建一个ThreadLocal的子类,重写initiaValue()方法。创建一个ThreadLocal子类最简单的方法就是在你创建ThreadLocal变量的地方简单地创建一个匿名子类。以下是一个示例:

private ThreadLocal myThreadLocal = new ThreadLocal<String>() {
@Override protected String initialValue() {
return String.valueOf(System.currentTimeMillis());
}
};

提供一个Supplier实现

第二种给Java ThreadLocal指定初始值的方法是使用它的静态工厂方法withInitial(Supplier)通过将Supplier接口作为一个参数实现。这个Supplier实现为ThreadLocal提供初始值。以下是一个示例:

ThreadLocal<String> threadLocal = ThreadLocal.withInitial(new Supplier<String>() {
@Override
public String get() {
return String.valueOf(System.currentTimeMillis());
}
});
ThreadLocal threadLocal = ThreadLocal.withInitial(
() -> { return String.valueOf(System.currentTimeMillis()); } );
ThreadLocal threadLocal3 = ThreadLocal.withInitial(
() -> String.valueOf(System.currentTimeMillis()) );

Lazy Setting ThreadLocal的值

在有些情况下,你不能使用标准的方法来设置初始值。比如,你可能需要一些配置信息,这些配置信息在你创建ThreadLocal时不可用。这种情况下你可以延迟设置初始值。以下是一个如何延迟设置初始值的示例:

public class MyDateFormatter {

private ThreadLocal<SimpleDateFormat> simpleDateFormatThreadLocal = new ThreadLocal<>();

public String format(Date date) {
SimpleDateFormat simpleDateFormat = getThreadLocalSimpleDateFormat();
return simpleDateFormat.format(date);
}


private SimpleDateFormat getThreadLocalSimpleDateFormat() {
SimpleDateFormat simpleDateFormat = simpleDateFormatThreadLocal.get();
if(simpleDateFormat == null) {
simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
simpleDateFormatThreadLocal.set(simpleDateFormat);
}
return simpleDateFormat;
}
}

在Thread Pool或者ExecutorService中使用ThreadLocal

如果你计划在Java线程池或者Java ExecutorService的任务中使用ThreadLocal,请牢记一点,你不能保证线程会执行你的任务。然而,如果你需要确保对于同一个对象每个线程都使用它们自己的实例,那么这不是问题。这种情况下你可以在Thread Pool或者ExecutorService中使用ThreadLocal。

Full ThreadLocal示例

以下是完全可运行的Java ThreadLocal示例:

public class ThreadLocalExample {

public static void main(String[] args) {
MyRunnable sharedRunnableInstance = new MyRunnable();

Thread thread1 = new Thread(sharedRunnableInstance);
Thread thread2 = new Thread(sharedRunnableInstance);

thread1.start();
thread2.start();

thread1.join(); //wait for thread 1 to terminate
thread2.join(); //wait for thread 2 to terminate
}

}
public class MyRunnable implements Runnable {

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

@Override
public void run() {
threadLocal.set( (int) (Math.random() * 100D) );

try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}

System.out.println(threadLocal.get());
}
}

可继承的ThreadLocal

InheritableThreadLocal类是ThreadLocal类的一个子类。

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store