为什么Java线程局部变量应该是静态的
我在这里读Threadlocal的JavaDoc 上面写着 ThreadLocal实例通常是希望将状态与线程(例如,用户ID或事务ID)关联的类中的私有静态字段为什么Java线程局部变量应该是静态的,java,multithreading,Java,Multithreading,我在这里读Threadlocal的JavaDoc 上面写着 ThreadLocal实例通常是希望将状态与线程(例如,用户ID或事务ID)关联的类中的私有静态字段 但我的问题是,他们为什么选择将其设置为静态(通常)-设置“每线程”状态会让人有点困惑,但字段是静态的?因为如果它是实例级字段,那么它实际上将是“每线程-每实例”,而不仅仅是保证的“每线程”这通常不是你想要的语义 通常,它包含一些对象,这些对象的作用域是用户对话、Web请求等。您不希望它们也被划分为类实例的子作用域。 一个web请求=>
但我的问题是,他们为什么选择将其设置为静态(通常)-设置“每线程”状态会让人有点困惑,但字段是静态的?因为如果它是实例级字段,那么它实际上将是“每线程-每实例”,而不仅仅是保证的“每线程”这通常不是你想要的语义 通常,它包含一些对象,这些对象的作用域是用户对话、Web请求等。您不希望它们也被划分为类实例的子作用域。
一个web请求=>一个持久性会话。
不是一个web请求=>每个对象一个持久性会话。原因是变量是通过与线程关联的指针访问的。它们的行为类似于具有线程范围的全局变量,因此static是最合适的。这是在pthreads中获取线程本地状态的方法,因此这可能只是历史和实现的一个意外情况。不必如此。重要的是,它应该是一个单例。将其设置为静态,或者如果您试图避免类中的任何静态字段,请将类本身设置为单例,然后您可以安全地使用实例级别的ThreadLocal,只要该单例在全局可用。在每个实例上使用ThreadLocal线程是指,如果您希望某个对象在所有方法中都可见,并使其具有线程安全性,而无需像对普通字段那样同步对其的访问 请参阅,这样可以更好地理解 简而言之,
ThreadLocal
对象的工作方式类似于键值映射。当线程调用ThreadLocal
get/set
方法时,它将检索/存储映射键中的线程对象,以及映射值中的值。这就是为什么不同的线程具有不同的复制值(您希望本地存储),因为它位于不同的映射条目中
这就是为什么只需要一个映射来保留所有值。虽然没有必要,但您也可以使用多个映射(无需声明static)来保留每个线程对象,这是完全冗余的,这就是为什么首选静态变量。
静态最终ThreadLocal
变量是线程安全的
static
使ThreadLocal变量仅可用于各个线程的多个类。这是一种全局变量,它是各个线程局部变量在多个类之间的衰减
我们可以使用下面的代码示例检查此线程的安全性
-将当前用户id存储在ThreadLocal中CurrentUser
-使用方法-TestService
从CurrentUser获取当前用户的简单服务getUser()
-此类用于创建多个线程并同时设置用户标识TestThread
main
执行结束并将当前用户打印为“62”,并行ForkJoinPool.commonPool-worker-1执行结束并将当前用户打印为“87”,并行ForkJoinPool.commonPool-worker-2执行结束并将当前用户打印为“31”,
并行ForkJoinPool.commonPool-worker-3
执行结束并将当前用户打印为“81”
并发线程能够检索正确的用户ID,即使它声明为“静态最终ThreadLocal”我喜欢这个解释,因为它显示了如何使用ThreadLocal。每个实例的线程都可以是一个有用的语义,但该模式的大多数用法都会涉及太多对象,因此最好使用
ThreadLocal
来保存对散列集的引用,该散列集将对象映射到每个线程实例。@可选,它只是意味着非静态ThreadLocal
的每个实例都会保存自己的线程本地数据,即使这些数据是ThreadLocal
实例存在于同一线程中。这样做并不一定是错误的——我想这可能是两种模式中最不受欢迎的一种
public class CurrentUser
public class CurrentUser {
private static final ThreadLocal<String> CURRENT = new ThreadLocal<String>();
public static ThreadLocal<String> getCurrent() {
return CURRENT;
}
public static void setCurrent(String user) {
CURRENT.set(user);
}
}
public class TestService {
public String getUser() {
return CurrentUser.getCurrent().get();
}
}
import java.util.ArrayList;
import java.util.List;
public class TestThread {
public static void main(String[] args) {
List<Integer> integerList = new ArrayList<>();
//creates a List of 100 integers
for (int i = 0; i < 100; i++) {
integerList.add(i);
}
//parallel stream to test concurrent thread execution
integerList.parallelStream().forEach(intValue -> {
//All concurrent thread will set the user as "intValue"
CurrentUser.setCurrent("" + intValue);
//Thread creates a sample instance for TestService class
TestService testService = new TestService();
//Print the respective thread name along with "intValue" value and current user.
System.out.println("Start-"+Thread.currentThread().getName()+"->"+intValue + "->" + testService.getUser());
try {
//all concurrent thread will wait for 3 seconds
Thread.sleep(3000l);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//Print the respective thread name along with "intValue" value and current user.
System.out.println("End-"+Thread.currentThread().getName()+"->"+intValue + "->" + testService.getUser());
});
}
}
Start-main->62->62
Start-ForkJoinPool.commonPool-worker-2->31->31
Start-ForkJoinPool.commonPool-worker-3->81->81
Start-ForkJoinPool.commonPool-worker-1->87->87
End-main->62->62
End-ForkJoinPool.commonPool-worker-1->87->87
End-ForkJoinPool.commonPool-worker-2->31->31
End-ForkJoinPool.commonPool-worker-3->81->81
Start-ForkJoinPool.commonPool-worker-2->32->32
Start-ForkJoinPool.commonPool-worker-3->82->82
Start-ForkJoinPool.commonPool-worker-1->88->88
Start-main->63->63
End-ForkJoinPool.commonPool-worker-1->88->88
End-main->63->63
...