【问题标题】:What is the Use and need of thread local线程本地的用途和需要是什么
【发布时间】:2013-06-05 05:23:48
【问题描述】:

我正在探索 Java 中的本地线程。我不明白为什么我们需要这门课。如果我只是简单地将一个新对象传递给每个线程以执行,我可以实现相同的目的,因为如果我使用 initialValue() 会发生同样的事情。我只是在 initialvalue() 中为每个线程返回一个新对象。

但是假设我有两个线程,ThreadOne:A 和 ThreadTwo B。现在我希望他们拥有自己的 SimpleDateFormat 类的副本。我可以通过在 ThreadLocal 类中扭曲 SimpleDateFormat 的对象然后使用 initialValue() 我可以返回 new SimpleDateFormat("yyyyMMdd HHmm"); 来做到这一点。我可以通过创建两个新的 SimpleDateFormat 对象并[将一个对象分别传递给 ThreadOne :A. 和 ThreadTwo :B. ThreadLocal 如何帮助我,从而实现相同的目的

问候,

【问题讨论】:

  • 这里有一些不错的答案:stackoverflow.com/questions/817856/…
  • +1 提问。在编写多线程应用程序的大约 30 年中,我从未见过有说服力的关于线程局部变量的论据,我也从未觉得有必要使用它们。如果我需要一个“本地线程”的东西,那么它作为线程类的成员进入 - 所有成员函数都可以访问它,每个线程一个“东西”,工作完成。
  • 感谢 +1 :) 。但我仍然认为我需要在他的主题上进行更多探索才能得到满意的答案。

标签: java multithreading thread-local


【解决方案1】:

已经有一些很好的例子here 供您提问。

但我尝试解释第二部分:

但是假设我有两个线程,ThreadOne:A 和 ThreadTwo B。现在我想要 他们拥有自己的 SimpleDateFormat 类的副本。我可以 这是通过在 ThreadLocal 类中扭曲 SimpleDateFormat 的对象 然后使用 initialValue() 我可以返回 new SimpleDateFormat("yyyyMMdd HHmm");。我可以通过相同的动机实现 创建两个 SimpleDateFormat 的新对象并 [将每个传递给 ThreadOne : A. 和 ThreadTwo : B. ThreadLocal 如何帮助我更多

通常,您需要使用某种格式来格式化日期,当然最好创建一次SimpleDateFormat 对象(而不是每次需要格式化日期时都创建一个新的SimpleDateFormat)。

所以你可能有这样的事情:

public class DateUtils {  
    private final static DateFormat dateFormat = new SimpleDateFormat("dd-mm-yyyy");  

    public String formatDate(Date date) {  
        return dateFormat.format(date);  
    }  
}  

如果多个线程同时调用formatDate(...),这将失败(您可能会得到奇怪的输出或exceptions),因为SimpleDateFormat 不是Thread-Safe。为了使其线程安全,您可以使用ThreadLocal:

public class DateUtils {  
    private final ThreadLocal<DateFormat> dateFormat = new ThreadLocal<DateFormat>() {  
        @Override  
        protected DateFormat initialValue() {  
            return new SimpleDateFormat("dd-mm-yyyy");  
        }  
    };  

    public String formatDate(Date date) {  
        return dateFormat.get().format(date);  
    }  
}  

现在对 formatDate() 方法的每个线程(或调用)都将在本地副本上工作,并且不会相互干扰。这为您提供了线程安全的行为。

【讨论】:

  • 感谢您的回复。但是我还不清楚一件事。

    public String formatDate(Date date) { return new SimpleDateFormat("dd-mm-yyyy").format(date); } 这也会产生同样的效果。返回新的 SimpleDateFormat("dd-mm-yyyy"); from initialValue() 具有为每个线程创建新对象的效果。那么 ThreadLocal 在这种情况下如何有用。它的正确用法是什么。 ?

  • @nits.kk 你找到满意的解释了吗?我仍然不知道在一个类中保留“私有 ThreadLocal”和“私有静态 Map”有什么区别
【解决方案2】:

线程局部存储服务于单个线程上下文中的全局变量。

考虑这个例子:你编写了一个多线程程序来处理用户请求。多个用户可以同时发起请求;您的系统为每个用户使用一个线程。

当用户请求到达时,您的系统会找出它来自的用户,并为该用户创建UserPermissions 对象的实例。

有几种方法可以使该对象对正在运行的程序可用。一种方法是将UserPermissions 传递给可能需要它的每个方法,以及直接或间接调用可能需要它的方法的每个方法。这可能会有问题,尤其是在使用回调的上下文中。

如果您的程序不是多线程的,您会在全局变量中设置UserPermissions。不幸的是,您不能这样做,因为多个用户请求可能同时处于活动状态。

这是线程本地存储的用武之地:创建用户权限的进程将UserPermissions对象设置在线程本地存储中,并将其保留在那里,直到请求处理结束。这样所有方法都可以根据需要获取UserPermissions,而无需将它们作为方法参数传递。

【讨论】:

  • UserPermissions 作为线程类数据成员/字段有什么问题?这样,所有线程类方法都可以根据需要获取 UserPermissions,而无需将它们作为方法参数传递。
  • @MartinJames 这种方法的一个大问题是需要对线程进行子类化。访问用户信息的需求“跨越”多个类,其中一些可能与线程实现类无关。
【解决方案3】:

您将使用ThreadLocal将“数据”传递给特定线程。
例如你有一个方法doSomething(SomeObject a, SomeOtherObject b);
除了ab 之外,您还可以通过线程安全的方式以线程安全的方式将更多信息传递给此方法,以实现特定 线程的执行

【讨论】:

  • 没有帮助。将新数据加载到线程本地并不比将新数据加载到线程类实例的数据字段中或多或少是线程安全的,即。不是线程安全的,通常需要同步。
  • @MartinJames:不,这是错误的。Threadlocal 是唯一的,特定线程可以访问。不需要同步
  • 什么数据?能否提供一个具体的例子?
【解决方案4】:

线程本地是实现dynamic scoping 的一种廉价方式。使用动态范围,在评估代码块期间存在绑定。在这种情况下,绑定存在于特定线程的执行期间。早期的 lisps 支持动态作用域,但它很少有意义,因此大多数现代编程语言不支持它,除了通过线程本地化。

动态范围/线程局部变量对于维护无处不在的上下文信息很有用,例如:

有一个名为“context-oriented programming”的研究旨在更好地支持编程语言中的此类问题。论文“context-oriented programming: beyond layers”展示了一些进一步的例子。

【讨论】:

    猜你喜欢
    • 2015-09-29
    • 2016-06-12
    • 2010-09-11
    • 2013-02-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-15
    • 2021-10-01
    相关资源
    最近更新 更多