书中的两个片段(并发章节):
class Accessor implements Runnable {
private final int id;
public Accessor(int idn) {
id = idn;
}
public void run() {
while(!Thread.currentThread().isInterrupted()) {
ThreadLocalVariableHolder.increment();
System.out.println(this);
Thread.yield();
}
}
public String toString() {
return "#" + id + ": " +
ThreadLocalVariableHolder.get();
}
}
public class ThreadLocalVariableHolder {
private static ThreadLocal<Integer> value =
new ThreadLocal<Integer>(); /*{
private Random rand = new Random(47);
protected synchronized Integer initialValue() {
return rand.nextInt(10000);
}
};*/
public static void increment() {
value.set(value.get() + 1);
}
public static int get() {
return value.get();
}
public static void main(String[] args) throws Exception {
ExecutorService exec = Executors.newCachedThreadPool();
for(int i = 0; i < 5; i++)
exec.execute(new Accessor(i));
TimeUnit.SECONDS.sleep(3); // Run for a while
exec.shutdownNow();
// All Accessors will quit
}
}
在类 ThreadLocalVariableHolder 中是内部类 ThreadLocal ,其方法为 initialValue ,属性为 rand ,未明确使用。如果我评论课程的全部内容(就像我在这里所做的那样),它仍会产生类似的结果。我想,这段代码已经过时了。你有没有理解,为什么这个代码在内部类中,目的是什么?
答案 0 :(得分:1)
我怀疑,如果我运行你的代码,它会在increment() -> value.get()
哪个有效,因为ThreadLocal
中的默认实施将返回null
protected T initialValue() {
return null;
}
来自JavaDoc
返回此线程局部变量的当前线程的“初始值”。当线程第一次使用get方法访问变量时,将调用此方法,除非线程先前调用了set方法,在这种情况下,不会为线程调用initialValue方法。通常,每个线程最多调用一次此方法,但是如果后续调用remove后跟get,则可以再次调用此方法。
此实现只返回null ;如果程序员希望线程局部变量具有非null的初始值,则必须对ThreadLocal进行子类化,并且重写此方法。通常,将使用匿名内部类。
答案 1 :(得分:0)
因为这个样本的行为是强大的,所以我不会删除这个错误的代码。重现它。取消注释内部类中的注释行,编译它,而不是注释回来并尝试它。我不得不去构建部分并删除.class文件以获取NullPointerException。可在NetBeans 8.0,JDK 1.7上重现。可能是因为内部类以某种方式报告错误绑定。
有关此示例的说明。 ThreadLocal类是在java.lang中编写的类,它具有特殊功能ThreadLocal,这些功能并不常见:
此类提供线程局部变量。这些变量不同 来自他们的正常对应者,每个访问一个的线程 (通过其get或set方法)有自己的,独立初始化 变量的副本。 ThreadLocal实例通常是私有的 希望将状态与线程关联的类中的静态字段 (例如,用户ID或交易ID)。
我必须说,编写自己的类比编写ThreadLocal类更容易,即使原始解决方案更简洁也是如此。