【问题标题】:How does ThreadLocal usage reduce reusabilityThreadLocal 的使用如何降低可重用性
【发布时间】:2012-07-02 06:13:10
【问题描述】:

广受好评的书 JCIP 谈到了 ThreadLocal 的使用:

很容易滥用 ThreadLocal,将其线程限制属性视为使用全局变量的许可或创建“隐藏”方法参数的一种手段。 线程局部变量会降低可重用性并在类之间引入隐藏的耦合,因此应谨慎使用。

说线程局部变量会降低可重用性并在类之间引入隐藏耦合是什么意思?

【问题讨论】:

  • 我用一个简单的代码示例对我的答案进行了编辑。

标签: java multithreading concurrency synchronization thread-local


【解决方案1】:

它们以与全局变量相同的方式降低可重用性:当您的方法的计算依赖于方法外部的状态,但不作为参数传递(例如类字段)时,您的方法的可重用性较低,因为它与它所在的对象/类的状态紧密耦合(或者更糟的是,完全在不同的类上)。

编辑:好的,这是一个更清楚的示例。我只是为了这个问题而使用了ThreadLocal,但它通常适用于全局变量。假设我想在多个线程上并行计算前 N 个整数的总和。我们知道最好的方法是计算每个线程的局部总和,并在最后将它们相加。出于某种原因,我们决定每个Taskcall 方法将使用在不同类中定义为全局(静态)变量的ThreadLocal sum 变量:

class Foo {
    public static ThreadLocal<Long> localSum = new ThreadLocal<Long>() {
        public Long initialValue() {
            return new Long(0);         
        }
    };
}

class Task implements Callable<Long> {

    private int start = 0;
    private int end = 0;    

    public Task(int start, int end) {
        this.start = start;
        this.end = end;
    }

    public Long call() {
        for(int i = start; i < end; i++) {
            Foo.localSum.set(Foo.localSum.get() + i);
        }
        return Foo.localSum.get();
    }
}

代码正常运行,并为我们提供了全局总和的预期值,但我们注意到Task 类及其call 方法现在与Foo 类严格耦合。如果我想在另一个项目中重用Task 类,我还必须移动Foo 类,否则代码将无法编译。

虽然这是一个故意复杂的简单示例,但您可以看到“隐藏”全局变量的危险。它也会影响可读性,因为阅读代码的其他人也必须搜索类 Foo 并查看 Foo.localSum 的定义是什么。你应该让你的类尽可能的独立。

【讨论】:

  • @Subhra - 都铎王朝的回答不够清楚吗?为什么您特别需要“示例代码”?
【解决方案2】:

每个线程声明一个ThreadLocal - 通常每个该类的对象声明一个字段 - 从这个开始 - 如果ThreadLocal 被滥用,可能会出现很多问题。

如果一个线程通过多个对象(单个类或多个类),则该线程使用的ThreadLocal 在所有这些实例中都是同一个实例。这就是BG所说的耦合。一旦出现耦合 - 可重用性就变得困难且容易出错。

【讨论】:

  • 关于 ThreadLocal 的 Java 文档说,通常 ThreadLocals 应该是私有静态的,但 ThreadLocal 不强制执行“静态”。所以从技术上讲 ThreadLocal 可以是每个线程每个实例。但是,您的第二段确实很好地回答了这个问题。
猜你喜欢
  • 1970-01-01
  • 2020-01-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-17
  • 2012-03-09
相关资源
最近更新 更多