广受好评的书JCIP说这关于 ThreadLocal 的使用:
通过将其线程限制属性视为使用全局变量的许可或作为创建“隐藏”方法参数的一种手段,很容易滥用 ThreadLocal。线程局部变量会降低可重用性并在类之间引入隐藏的耦合,因此应谨慎使用。
说线程局部变量会降低可重用性并在类之间引入隐藏耦合是什么意思?
广受好评的书JCIP说这关于 ThreadLocal 的使用:
通过将其线程限制属性视为使用全局变量的许可或作为创建“隐藏”方法参数的一种手段,很容易滥用 ThreadLocal。线程局部变量会降低可重用性并在类之间引入隐藏的耦合,因此应谨慎使用。
说线程局部变量会降低可重用性并在类之间引入隐藏耦合是什么意思?
它们以与全局变量几乎相同的方式降低可重用性:当您的方法的计算依赖于方法外部的状态,但不作为参数传递(例如类字段)时,您的方法的可重用性较低,因为它是紧密耦合的到它所在的对象/类的状态(或者更糟,完全在不同的类上)。
编辑:好的,这是一个更清楚的例子。我ThreadLocal
只是为了这个问题而使用它,但它通常适用于全局变量。假设我想在多个线程上并行计算前 N 个整数的总和。我们知道最好的方法是计算每个线程的局部总和,并在最后将它们相加。出于某种原因,我们决定call
each 的方法Task
将使用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
是什么。你应该让你的类尽可能的独立。
每个线程都声明A ThreadLocal
- 通常每个该类的对象声明一个字段 - 从这个开始 - 如果ThreadLocal
滥用可能会出现很多错误。
如果一个线程通过多个对象(单个类或多个类),则ThreadLocal
该线程使用的对象是所有这些实例中的同一个实例。这就是BG所说的耦合。有耦合的那一刻 - 可重用性变得困难且容易出错。