ThreadLocal 的目的?

发布于 2024-08-06 06:21:17 字数 226 浏览 5 评论 0 原文

此处给出的 ThreadLocal 的目的是该变量对于访问包含 ThreadLocal 变量的对象的任何线程来说都是本地的。将 ThreadLocal 变量作为类的成员,然后将其设置为线程的本地变量,与将本地变量设置为线程本身的本地变量有何区别?

The purpose of ThreadLocal as given here states that the variable is local to any Thread accessing an object containing the ThreadLocal variable. What difference does it make, in having a ThreadLocal variable as a member of a class and then making it local to a Thread, rather than having a local variable to the Thread itself?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

梦过后 2024-08-13 06:21:17

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

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

doGet(HttpServletRequest req, HttpServletResponse resp) {
  User user = getLoggedInUser(req);
  doSomething()
  doSomethingElse()
  renderResponse(resp)
}

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

doGet(HttpServletRequest req, HttpServletResponse resp) {
  User user = getLoggedInUser(req);
  doSomething(user)
  doSomethingElse(user)
  renderResponse(resp,user)
}

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

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

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

User user = StaticClass.getThreadLocal().get()

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

编辑:静态类的代码

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

  static ThreadLocal<User> getThreadLocal() {
    return threadLocal;
  }
}

A thread is a unit of execution and so multiple thread can execute the same code at the same time. If multiple threads execute on an object/instance at the same time they will share the instance variables. Each thread will have its own local variables but it is difficult to share these across objects without passing parameters.

It is best explained by way of an example. Say you have a Servlet that gets the logged in user and then executes some code.

doGet(HttpServletRequest req, HttpServletResponse resp) {
  User user = getLoggedInUser(req);
  doSomething()
  doSomethingElse()
  renderResponse(resp)
}

Now what happens if the doSomething() methods needs access to the user object? You can't make the user object an instance or static variable because each thread will then use the same user object. You could pass the user object around as a parameter but this quickly becomes messy and leaks user objects into every method call:

doGet(HttpServletRequest req, HttpServletResponse resp) {
  User user = getLoggedInUser(req);
  doSomething(user)
  doSomethingElse(user)
  renderResponse(resp,user)
}

A more elegant solution is to put the user object into a ThreadLocal

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

Now any code that requires the user object at any time can get hold of it by extracting it from the thread local, without needing to resort to those pesky extra parameters:

User user = StaticClass.getThreadLocal().get()

If you use this approach be mindful to remove the objects again in a finally block. Otherwise the user object might hang around in environments that use a Thread Pool (like Tomcat app server).

Edit: The code for static class

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

  static ThreadLocal<User> getThreadLocal() {
    return threadLocal;
  }
}
白馒头 2024-08-13 06:21:17

您必须认识到,扩展 Thread 的类的实例实际的 Java 线程不同(可以将其想象为运行的“执行指针”)通过您的代码并执行它)。

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

当然,您可以尝试将成员保持为私有,并确保它仅由 run() 或从中调用的方法使用(公共方法也可以从其他线程调用),但这是容易出错,并且对于一个更复杂的系统来说并不可行,在该系统中您不想将所有数据保留在 Thread 子类中(实际上您不应该子类化 Thread,而是使用 Runnable 代替)。

ThreadLocal 是一种简单、灵活的方法,可以让每线程数据无法被其他线程同时访问,而不需要付出巨大的努力或设计妥协。

You have to realize that an instance of a class that extends Thread is not the same thing as an actual Java thread (which can be imagined as a "execution pointer" that runs through your code and executes it).

Instances of such a class represent a Java thread and allow to manipulate it (e.g. interrupt it), but apart from that they are just regular objects, and their members can be accessed from all threads that can get hold of a reference to the object (which is not hard).

Of course you can try to keep a member private and make sure that it's only used by the run() or methods called from it (public methods can be called from other threads as well), but this is error-prone and not really feasible for a more complex system where you don't want to keep data all in a Thread subclass (actually you're not supposed to subclass Thread, but to use Runnable instead).

ThreadLocal is a simple, flexible way to have per-thread data that cannot be accessed concurrently by other threads, without requiring great effort or design compromises.

不知所踪 2024-08-13 06:21:17

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

让我解释一下将结果与特定线程相关联的想法。 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);
  }
}

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

A Thread object can have internal data members but these are accessible to anyone who has (or can get) a reference to the Thread object. A ThreadLocal is deliberately associated only with the each Thread that accesses it. The advantage is that there are no concurrency issues (within the context of the ThreadLocal). A Thread's internal data member has all the same concurrency issues any shared state does.

Let me explain the idea of associating a result with a particular thread. The essence of a ThreadLocal is something like this:

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);
  }
}

Now there's more to it than that but as you can see the value returned is determined by the current thread. That's why it's local to each thread.

肥爪爪 2024-08-13 06:21:17

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

ThreadLocal is very useful in webapplications. The typical pattern is that somewhere at the start of the processing of a web request (usually in a servlet filter) state is stored in a ThreadLocal variable. Because all of the processing for a request is done in 1 thread all components participating in the request have access to this variable.

不必了 2024-08-13 06:21:17

维基百科有一个关于此问题领域的条目。在我们的环境中,它通常用于将请求保留在本地。在服务器端,请求主要由单个线程处理。为了保持本地化,您可以将数据(例如会话数据)放在线程本地变量中。此数据对其他请求(线程)不可见,因此您不需要将其与其他请求同步。

并且不要忘记,有一些 JAVA API 构造是线程安全的,例如 日期格式。 DateFormat 的静态实例在服务器端不起作用。

事实上,当您使用自己的私有数据副本时,处理多线程编程比使用锁和监视器进行处理更容易。

There is a wikipedia entry about this problem area. In our environment it's usually used to keep things local to request. On the server side a request is handled mostly by a single thread. To keep things local you put your data, e.g. the session data, in a thread local variable. This data is invisible to the other request (threads) and therefore you don't need to synchronize it with the other requests.

And donn't forget, there are JAVA API constructs, that are not thread safe, e.g. DateFormat. A static instance of DateFormat just doesn't work on the server side.

In fact, it's easier to handle multi threaded programming when you use your own private copy of data than handling with locks and monitors.

我的痛♀有谁懂 2024-08-13 06:21:17

ThreadLocals 的优点是它们可以通过在普通线程或任何线程子类上运行的方法使用。

相比之下,如果您的线程局部变量必须实现为 Thread 的自定义子类的成员,则有很多事情您无法执行。例如,如果您的应用程序需要在预先存在的普通 Thread 实例上运行方法,那么您的应用程序将会遇到麻烦;即由应用程序编写者未编写且无法修改的某些库代码创建的实例。

The advantage of ThreadLocals is that they are usable by methods run on plain vanilla Threads ... or any subclass of Thread.

By contrast, if your thread locals have to be implemented as members of a custom subclass of Thread, there are a lot of things you cannot do. For example, your application will be in trouble if it needs to run methods on pre-existing vanilla Thread instances; i.e. instances that were created by some library code that the application writer did not write, and cannot modify.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文