备受好评的书籍JCIP说明了ThreadLocal的用法:
通过将线程限制属性视为使用全局变量的许可证或创建“隐藏”方法参数的方法,很容易滥用ThreadLocal。 线程局部变量会降低可重用性并在类之间引入隐藏的耦合,因此应谨慎使用。
说Thread-local变量可以减少可重用性并在类之间引入隐藏的耦合是什么意思?
答案 0 :(得分:11)
它们以与全局变量相同的方式减少可重用性:当方法的计算依赖于方法外部的状态,但不作为参数传递(例如类字段)时,您的方法不太可重用,因为它与它所在的对象/类的状态紧密耦合(或者更糟糕的是,完全在另一个类上)。
修改:好的,这是一个让它更清晰的例子。我只是为了问题而使用ThreadLocal
,但它一般适用于全局变量。假设我想在几个线程上并行计算前N个整数的总和。我们知道最好的方法是计算每个线程的本地总和,然后在最后总结它们。出于某种原因,我们决定每个call
的{{1}}方法将使用在不同类中定义的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
的定义。你应该尽可能保持自己的课程。
答案 1 :(得分:3)
每个线程声明一个ThreadLocal
- 通常会为该类的每个对象声明一个字段 - 从此开始 - 如果ThreadLocal
被滥用,可能会出现很多错误
如果线程通过多个对象(单个类或多个类),则此线程使用的ThreadLocal
在所有这些实例中都是相同的实例。这是BG正在谈论的耦合。在存在耦合的那一刻 - 可重用性变得困难且容易出错。