Java ThreadLocal用于创建线程局部变量。我们知道,一个对象的所有线程都会共享其变量,所以该变量不是线程安全的。我们可以使用同步来实现线程安全,但如果我们想避免同步,我们可以使用ThreadLocal变量。
TheadLocal结构允许我们存储只有特定线程才能访问的数据。
ThreadLocal实例通常是希望将状态与线程相关联的类中的私有静态字段(例如,用户ID或事务ID)。
T get() - 返回该线程本地变量的当前线程副本中的值。
*受保护的T initialValue() - 返回当前线程的这个线程局部变量的 "初始值"。
void remove() - 移除当前线程对该线程局部变量的值。
void set(T value) - 将当前线程对该线程局部变量的拷贝设置为指定的值。
在这个例子中,下面的类生成了每个线程的本地唯一标识符。一个线程的ID在它第一次调用*ThreadId.get()*时被分配,并在后续调用中保持不变。
import java.util.concurrent.atomic.AtomicInteger;
public class ThreadLocalExample {
// Atomic integer containing the next thread ID to be assigned
private static final AtomicInteger nextId = new AtomicInteger(0);
// Thread local variable containing each thread's ID
private static final ThreadLocal<Integer> threadId = new ThreadLocal<Integer>() {
@Override
protected Integer initialValue() {
return nextId.getAndIncrement();
}
};
// Returns the current thread's unique ID, assigning it if necessary
public static int get() {
return threadId.get();
}
}
只要线程还活着,并且ThreadLocal实例可被访问,每个线程都持有对其线程-本地变量副本的隐式引用;在一个线程离开后,其所有线程-本地实例的副本都会被垃圾回收(除非存在对这些副本的其他引用)。
在实际的Java应用中,我们使用ThreadLocal来存储登录用户Context实例。每个线程都会有自己的ThreadLocal实例。
在我们的例子中,我们为每个特定的userId建立了一个专用线程,这个线程是由我们创建的,所以我们可以完全控制它。
run()方法将获取用户上下文,并使用set()方法将其存储到ThreadLocal变量中。
让我们首先创建一个包含userName的Context类。
public class Context {
private final String userName;
Context(String userName) {
this.userName = userName;
}
@Override
public String toString() {
return "Context{" +
"userNameSecret='" + userName + '\'' +
'}';
}
}
public class UserRepository {
String getUserNameForUserId(Integer userId) {
return UUID.randomUUID().toString();
}
}
public class ThreadLocalWithUserContext implements Runnable {
private static final ThreadLocal<Context> userContext = new ThreadLocal<>();
private final Integer userId;
private UserRepository userRepository = new UserRepository();
ThreadLocalWithUserContext(Integer userId) {
this.userId = userId;
}
@Override
public void run() {
String userName = userRepository.getUserNameForUserId(userId);
userContext.set(new Context(userName));
System.out.println("thread context for given userId: " + userId + " is: " + userContext.get());
}
}
我们可以通过启动两个线程来测试它,这两个线程将为一个给定的userId执行动作。
public class ThreadLocalTest{
public static void main(String []args){
ThreadLocalWithUserContext firstUser = new ThreadLocalWithUserContext(1);
ThreadLocalWithUserContext secondUser = new ThreadLocalWithUserContext(2);
new Thread(firstUser).start();
new Thread(secondUser).start();
}
}
运行这段代码后,我们会在标准输出上看到ThreadLocal是按给定的线程设置的。
输出。
thread context for given userId: 1 is: Context{userNameSecret='18a78f8e-24d2-4abf-91d6-79eaa198123f'}
thread context for given userId: 2 is: Context{userNameSecret='e19f6a0a-253e-423e-8b2b-bca1f471ae5c'}
我们可以看到,每个用户都有自己的Context。
###参考
https://docs.oracle.com/javase/8/docs/api/java/lang/ThreadLocal.html
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://www.javaguides.net/2018/09/threadlocal-class-in-java.html
内容来源于网络,如有侵权,请联系作者删除!