【问题标题】:Class level, instance level and local ThreadLocals类级别、实例级别和本地 ThreadLocals
【发布时间】:2020-04-05 13:56:35
【问题描述】:

我了解类级别线程本地变量的意义。与线程相关联,我们需要在该线程的不同实例和类之间共享线程本地。所以我们需要使它们成为类级别。如果我们想在同一类的不同实例之间共享本地线程,我们可以将它们设为private static。如果我们想在不同的类之间共享本地线程,我们可以将它们设为public static

Q0.如果上面有错误请纠正我

我对实例范围 (non-static) 线程本地和本地(在某些方法中定义)线程本地表示怀疑:

Q1. 是否有任何有效的用例,例如范围 (non-static) 线程本地?
Q2. 是否有任何有效的本地用例 (在某个方法中定义的)线程局部变量?
Q3。当实例被垃圾回收时,实例范围 (non-static) 线程局部变量是否被删除?
Q4。方法返回时是否删除了本地(在某些方法中定义)线程本地?

【问题讨论】:

    标签: java multithreading


    【解决方案1】:

    ThreadLocal 当正确实现为静态变量时,本质上充当所有有权访问它的线程的实例变量。即使只有一个 ThreadLocal 变量,该机制也会使每个线程都有自己的值实例。

    因此

    第一季度。不,拥有ThreadLocal 范围内的实例没有意义。这并不意味着您不能编写将使用范围为TL 的实例的代码,但您需要跟踪(在您的开发人员心中)这两个实例 em> 和 thread 用于正确的功能,即使您找到代码可以解决的用例,也会有更好的方法来处理它。

    第二季度。不会。因为一个局部变量不能有多个线程访问它,所以它与常规局部变量没有区别。

    第三季度。 ThreadLocal<> 包装器变得无法访问,但实际变量仍包含在线程的映射中,正如您所说的那样。这会导致资源/内存泄漏,因为在线程停止之前无法清除它。

    第四季度。与 Q3 相同,如果您丢失了包装器引用,则会立即泄漏。如果您在某处分配引用,那只是奇怪的编程。方法本地 ThreadLocal 变量将是非常令人担忧的代码。

    无论如何,在现代代码(甚至是旧代码)中,您都不想过多地使用该类,并且它与反应式编程不兼容,但如果您确实使用它,那么它的用法就很简单了。单个ThreadLocal 最容易实现为类级变量。

    【讨论】:

    • 我相信所有类型的(类、实例和本地)线程局部变量都是在Thread.threadLocals map 中创建的。那么,当我们从一个方法返回时,是不是还有一个可以从Thread.threadLocals.tables array 访问的本地线程本地实例的活动引用,从而阻止了 GC?
    • 你的意思是说本地线程本地变量会阻止 GC,正如我在评论中所建议的那样?但是在您对 Q3 的回答中,您说常规 GC 适用。这是否意味着,防止本地线程 locals 的 GC 是根据常规 GC 行为?
    【解决方案2】:

    Q2.本地(在某些方法中定义)线程本地变量是否有任何有效用例?

    首先,让我们明确一点。如果您说“本地Foobar”(对于任何class Foobar),那么您在说什么并不完全清楚。 变量可以是“类级别”(即static)或“实例级别”或“本地”;但是Foobar 实例不是一个变量。 Java 程序中的变量只能引用在堆上分配的Foobar 实例。一个程序中有多个变量引用同一个实例非常简单,也很常见。

    ThreadLocal 是一个类,ThreadLocal 的实例是堆上的对象。同一个ThreadLocal 对象可以被static ThreadLocal 变量引用,同时也可以被一个或多个线程中的局部变量引用。

    当您说“本地ThreadLocal”时,您可能指的是一个本地变量,它包含对与共享的ThreadLocal 实例的引用其他线程,-OR- 您可能正在谈论一个 由一个局部变量引用的 ThreadLocal 实例。第二种情况没有任何意义,因为该实例不能被多个线程共享。


    Q1. 是否有任何有效的用例,例如作用域(非静态)线程局部变量?

    也许是这样,但我会称之为“code smell”。 (也就是说,有理由仔细查看代码,看看它是否可以更好地组织。)我个人永远不会在新代码中使用ThreadLocal。我唯一一次使用过它是在将较旧的单线程代码移植到多线程系统中时;当我这样做时,有问题的变量总是static(即类级别)变量。

    个人尽量不要在新代码中使用static,除非某些函数被明确标记为返回对“单例对象”的引用。


    Q3.、Q4. [...实例何时删除...]?

    一个实例有资格在引用它的程序中没有“活动”变量时被删除。可能发生的一种方法是,如果唯一引用它的变量是某个函数的局部变量,并且该函数返回。第二种可能发生的方式是,如果将引用该实例的唯一变量分配为引用其他实例。第三种方式是,如果对它的唯一引用来自其他对象的实例变量,并且所有这些其他对象本身,都可以被删除。

    在大多数 Java 实现中,实例在符合删除条件时不会立即被删除。实际删除将在一段时间后发生。何时,取决于 JRE 的 garbage collector 采用的策略以及程序使用对象的模式。

    【讨论】:

    • "第二种情况没有任何意义,因为该实例不能被多个线程共享。" - 但无论如何,线程本地永远不能被多个线程共享。我也知道垃圾收集是如何工作的。但是线程本地存储在Thread 类的threadlocals 映射变量中,该变量的类型为ThreadLocal.ThreadLocalMap。所以我在想如果没有对特定线程局部变量的活动引用,它会自动从这个threadlocals映射中自动删除。
    • @anir,你误会了。假设您有ThreadLocal<Foo> foo=new ThreadLocal<>(); 我说的是单个共享的ThreadLocal 对象,而不是它所指的几个特定于线程的Foo 对象。 ThreadLocal 对象是一个集合——就像ArrayListHashMap 一样,只是集合中的对象是由调用线程的ID 隐式选择的,而不是由调用线程的ID 显式选择的。给定“索引”或“键”。
    猜你喜欢
    • 2012-06-07
    • 1970-01-01
    • 1970-01-01
    • 2016-02-07
    • 2023-04-03
    • 1970-01-01
    • 2015-01-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多