ThreadLocal的目的?

Aja*_*jay 33 java multithreading thread-local

这里给出的ThreadLocal的目的表明该变量是访问包含ThreadLocal变量的对象的任何Thread的本地变量.将ThreadLocal变量作为类的成员然后将其作为Thread的本地变量而不是将本地变量赋予Thread本身,它会有什么不同?

leo*_*onm 80

线程是执行单元,因此多个线程可以同时执行相同的代码.如果多个线程同时在对象/实例上执行,则它们将共享实例变量.每个线程都有自己的局部变量,但很难在不传递参数的情况下跨对象共享这些变量.

最好通过一个例子来解释.假设您有一个获取登录用户的Servlet,然后执行一些代码.

doGet(HttpServletRequest req, HttpServletResponse resp) {
  User user = getLoggedInUser(req);
  doSomething()
  doSomethingElse()
  renderResponse(resp)
}
Run Code Online (Sandbox Code Playgroud)

现在,如果doSomething()方法需要访问用户对象会发生什么?您不能使用户对象成为实例或静态变量,因为每个线程将使用相同的用户对象.您可以将用户对象作为参数传递,但这会很快变得混乱,并将用户对象泄漏到每个方法调用中:

doGet(HttpServletRequest req, HttpServletResponse resp) {
  User user = getLoggedInUser(req);
  doSomething(user)
  doSomethingElse(user)
  renderResponse(resp,user)
}
Run Code Online (Sandbox Code Playgroud)

更优雅的解决方案是将用户对象放入ThreadLocal

doGet(HttpServletRequest req, HttpServletResponse resp) {
  User user = getLoggedInUser(req);
  StaticClass.getThreadLocal().set(user)
  try {
    doSomething()
    doSomethingElse()
    renderResponse(resp)
  }
  finally {
    StaticClass.getThreadLocal().remove()
  }
}
Run Code Online (Sandbox Code Playgroud)

现在任何需要用户对象的代码都可以通过从本地线程中提取它来获取它,而不需要求助于那些讨厌的额外参数:

User user = StaticClass.getThreadLocal().get()
Run Code Online (Sandbox Code Playgroud)

如果您使用此方法,请注意在finally块中再次删除对象.否则,用户对象可能会在使用线程池(如Tomcat应用服务器)的环境中闲逛.

编辑:静态类的代码

class StaticClass {
  static private ThreadLocal<User> threadLocal = new ThreadLocal<>();

  static ThreadLocal<User> getThreadLocal() {
    return threadLocal;
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 我不会说它总是更优雅,但它更为权宜之计 (10认同)
  • 什么是静态类?扩展 Thread 的类? (2认同)

Mic*_*rdt 13

你必须认识到,继承Thread类的一个实例是相同的事,作为一个实际的Java线程(它可以被想象成是通过您的代码运行,并成为一个"执行的指针").

这样一个类的实例代表一个Java线程并允许操作它(例如中断它),但除此之外它们只是常规对象,并且可以从可以获取对象引用的所有线程访问它们的成员(这并不难.

当然你可以尝试保持一个成员私有,并确保它只被run()它调用的或者方法使用(公共方法也可以从其他线程调用),但是这很容易出错,而且不太可行.复杂的系统,你不想在Thread子类中保存数据(实际上你不应该将Thread子类化,而是使用Runnable).

ThreadLocal是一种简单,灵活的方式,可以让其他线程无法同时访问每个线程数据,而无需花费大量精力或设计妥协.


cle*_*tus 8

Thread对象可以有内部数据成员,但任何拥有(或可以获得)Thread对象引用的人都可以访问这些成员.ThreadLocal只是故意与访问它的每个Thread相关联.优点是没有并发问题(在ThreadLocal的上下文中).Thread的内部数据成员具有所有共享状态所具有的相同并发性问题.

让我解释一下将结果与特定线程相关联的想法.ThreadLocal的本质是这样的:

public class MyLocal<T> {
  private final Map<Thread, T> values = new HashMap<Thread, T>();

  public T get() {
    return values.get(Thread.currentThread());
  }

  public void set(T t) {
    values.put(Thread.currentThread(), t);
  }
}
Run Code Online (Sandbox Code Playgroud)

现在还有更多内容,但正如您所看到的,返回的值由当前线程决定.这就是为什么它是每个线程的本地.


Kee*_*ter 6

ThreadLocal在Web应用程序中非常有用.典型的模式是在Web请求处理开始时(通常在servlet过滤器中)的某个状态存储在ThreadLocal变量中.由于请求的所有处理都在1个线程中完成,因此参与请求的所有组件都可以访问此变量.