陷阱 - 扩展 java.lang.Thread

Thread 类的 javadoc 显示了两种定义和使用线程的方法:

使用自定义线程类:

 class PrimeThread extends Thread {
     long minPrime;
     PrimeThread(long minPrime) {
         this.minPrime = minPrime;
     }

     public void run() {
         // compute primes larger than minPrime
          . . .
     }
 }

 PrimeThread p = new PrimeThread(143);
 p.start();

使用 Runnable

 class PrimeRun implements Runnable {
     long minPrime;
     PrimeRun(long minPrime) {
         this.minPrime = minPrime;
     }

     public void run() {
         // compute primes larger than minPrime
          . . .
     }
 }

 PrimeRun p = new PrimeRun(143);
 new Thread(p).start();

(来源: java.lang.Thread javadoc 。)

自定义线程类方法有效,但它有一些问题:

  1. 在使用经典线程池,执行器或 ForkJoin 框架的上下文中使用 PrimeThread 是很尴尬的。 (这并非不可能,因为 PrimeThread 间接实现了 Runnable,但使用自定义的 Thread 类作为 Runnable 肯定是笨拙的,并且可能不可行……取决于类的其他方面。)

  2. 其他方法存在更多错误的机会。例如,如果你在未委托 Thread.start() 的情况下声明了 PrimeThread.start(),那么你最终会得到一个在当前线程上运行的线程

将线程逻辑放入 Runnable 的方法避免了这些问题。实际上,如果你使用匿名类(Java 1.1 以上)来实现 Runnable,结果将比上面的示例更简洁,更易读。

 final long minPrime = ...
 new Thread(new Runnable() {
     public void run() {
         // compute primes larger than minPrime
          . . .
     }
 }.start();

使用 lambda 表达式(Java 8 以后),上面的示例将变得更加优雅:

 final long minPrime = ...
 new Thread(() -> {
    // compute primes larger than minPrime
     . . .
 }).start();