Bruce Eckel Thinking in Java第4版,ThreadLocal Variable Holder

时间:2014-08-20 09:13:59

标签: java multithreading

书中的两个片段(并发章节):

 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 ,未明确使用。如果我评论课程的全部内容(就像我在这里所做的那样),它仍会产生类似的结果。我想,这段代码已经过时了。你有没有理解,为什么这个代码在内部类中,目的是什么?

2 个答案:

答案 0 :(得分:1)

我怀疑,如果我运行你的代码,它会在increment() -> value.get()

处抛出NPE

哪个有效,因为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类更容易,即使原始解决方案更简洁也是如此。