Concurrency: Basic Threading (3)

9. Coding variations

9.1. 直接继承Thread

  创建任务除了通过实现Runnable接口,也可以通过直接继承Thread的方式来实现。

public class SimpleThread extends Thread {
private int countDown = 5;
private static int threadCount = 0;
public SimpleThread() {
// Store the thread name:
super(Integer.toString(++threadCount));
start();
}
public String toString() {
return "#" + getName() + "(" + countDown + "), ";
}
public void run() {
while(true) {
System.out.print(this);
if(--countDown == 0)
return;
}
}
public static void main(String[] args) {
for(int i = 0; i < 5; i++)
new SimpleThread();
}
}
/* Output
#1(5), #4(5), #4(4), #4(3), #5(5), #2(5), #3(5), #2(4), #2(3), #2(2), #2(1), #5(4),
#4(2), #4(1), #1(4), #5(3), #3(4), #5(2), #1(3), #5(1), #3(3), #3(2), #3(1), #1(2),
#1(1),
*/
public class SimpleThread extends Thread { private int countDown = 5; private static int threadCount = 0; public SimpleThread() { // Store the thread name: super(Integer.toString(++threadCount)); start(); } public String toString() { return "#" + getName() + "(" + countDown + "), "; } public void run() { while(true) { System.out.print(this); if(--countDown == 0) return; } } public static void main(String[] args) { for(int i = 0; i < 5; i++) new SimpleThread(); } } /* Output #1(5), #4(5), #4(4), #4(3), #5(5), #2(5), #3(5), #2(4), #2(3), #2(2), #2(1), #5(4), #4(2), #4(1), #1(4), #5(3), #3(4), #5(2), #1(3), #5(1), #3(3), #3(2), #3(1), #1(2), #1(1), */
public class SimpleThread extends Thread {
    private int countDown = 5;
    private static int threadCount = 0;
    public SimpleThread() {
        // Store the thread name:
        super(Integer.toString(++threadCount));
        start();
    }
    public String toString() {
        return "#" + getName() + "(" + countDown + "), ";
    }
    public void run() {
        while(true) {
            System.out.print(this);
            if(--countDown == 0)
                return;
        }
    }
    public static void main(String[] args) {
        for(int i = 0; i < 5; i++)
            new SimpleThread();
    }
}
/* Output
#1(5), #4(5), #4(4), #4(3), #5(5), #2(5), #3(5), #2(4), #2(3), #2(2), #2(1), #5(4),
#4(2), #4(1), #1(4), #5(3), #3(4), #5(2), #1(3), #5(1), #3(3), #3(2), #3(1), #1(2),
#1(1), 
*/

9.2. 自管理Runnable

  还有一种自管理Runnable的用法,由实现Runnable的类自己创建并开始线程。

public class SelfManaged implements Runnable {
private int countDown = 5;
private Thread t = new Thread(this);
public SelfManaged() { t.start(); }
public String toString() {
return Thread.currentThread().getName() + "(" + countDown + "), ";
}
public void run() {
while(true) {
System.out.print(this);
if(--countDown == 0)
return;
}
}
public static void main(String[] args) {
for(int i = 0; i < 5; i++)
new SelfManaged();
}
}
/* Output
Thread-0(5), Thread-4(5), Thread-3(5), Thread-2(5), Thread-1(5), Thread-2(4),
Thread-3(4), Thread-4(4), Thread-0(4), Thread-4(3), Thread-3(3), Thread-2(3),
Thread-1(4), Thread-2(2), Thread-3(2), Thread-4(2), Thread-0(3), Thread-4(1),
Thread-3(1), Thread-2(1), Thread-1(3), Thread-1(2), Thread-0(2), Thread-1(1),
Thread-0(1),
*/
public class SelfManaged implements Runnable { private int countDown = 5; private Thread t = new Thread(this); public SelfManaged() { t.start(); } public String toString() { return Thread.currentThread().getName() + "(" + countDown + "), "; } public void run() { while(true) { System.out.print(this); if(--countDown == 0) return; } } public static void main(String[] args) { for(int i = 0; i < 5; i++) new SelfManaged(); } } /* Output Thread-0(5), Thread-4(5), Thread-3(5), Thread-2(5), Thread-1(5), Thread-2(4), Thread-3(4), Thread-4(4), Thread-0(4), Thread-4(3), Thread-3(3), Thread-2(3), Thread-1(4), Thread-2(2), Thread-3(2), Thread-4(2), Thread-0(3), Thread-4(1), Thread-3(1), Thread-2(1), Thread-1(3), Thread-1(2), Thread-0(2), Thread-1(1), Thread-0(1), */
public class SelfManaged implements Runnable {
    private int countDown = 5;
    private Thread t = new Thread(this);
    public SelfManaged() { t.start(); }
    public String toString() {
        return Thread.currentThread().getName() + "(" + countDown + "), ";
    }
    public void run() {
        while(true) {
            System.out.print(this);
            if(--countDown == 0)
                return;
        }
    }
    public static void main(String[] args) {
        for(int i = 0; i < 5; i++)
            new SelfManaged();
    }
}
/* Output
Thread-0(5), Thread-4(5), Thread-3(5), Thread-2(5), Thread-1(5), Thread-2(4),
Thread-3(4), Thread-4(4), Thread-0(4), Thread-4(3), Thread-3(3), Thread-2(3),
Thread-1(4), Thread-2(2), Thread-3(2), Thread-4(2), Thread-0(3), Thread-4(1),
Thread-3(1), Thread-2(1), Thread-1(3), Thread-1(2), Thread-0(2), Thread-1(1),
Thread-0(1), 
*/

  通过实现Runnable来创建任务的好处是,可以同时继承其他类。

  上面的两个简单的例子都在构造器中调用start()开始线程。对于更加复杂的情况,在构造器中调用start()可能会带来问题:调用start()时任务已经在新的线程上开始执行,但构造器可能还没有执行结束,这时任务就有可能访问还没有初始化完成的资源。应尽量使用Executors而不要手动创建Thread

9.3. 内部类继承Thread

  使用内部类继承Thread的方式,可以在任务中访问外部成员。

class InnerThread1 {
private int countDown = 5;
private Inner inner;
private class Inner extends Thread {
Inner(String name) {
super(name);
start();
}
public void run() {
try {
while(true) {
System.out.println(this);
if(--countDown == 0) return;
sleep(10);
}
} catch(InterruptedException e) {
System.out.println("interrupted");
}
}
public String toString() {
return getName() + ": " + countDown;
}
}
public InnerThread1(String name) {
inner = new Inner(name);
}
}
class InnerThread1 { private int countDown = 5; private Inner inner; private class Inner extends Thread { Inner(String name) { super(name); start(); } public void run() { try { while(true) { System.out.println(this); if(--countDown == 0) return; sleep(10); } } catch(InterruptedException e) { System.out.println("interrupted"); } } public String toString() { return getName() + ": " + countDown; } } public InnerThread1(String name) { inner = new Inner(name); } }
class InnerThread1 {
    private int countDown = 5;
    private Inner inner;
    private class Inner extends Thread {
        Inner(String name) {
            super(name);
            start();
        }
        public void run() {
            try {
                while(true) {
                    System.out.println(this);
                    if(--countDown == 0) return;
                    sleep(10);
                }
            } catch(InterruptedException e) {
                System.out.println("interrupted");
            }
        }
        public String toString() {
            return getName() + ": " + countDown;
        }
    }
    public InnerThread1(String name) {
        inner = new Inner(name);
    }
}

这里InnerThread1的内部类Inner继承Thread,在其run()中可以访问InnerThread1countDown

9.4. 匿名内部类继承Thread

  如果只是为了获得多线程的能力,继承Thread的内部类的名字并不重要,可以使用匿名内部类。

class InnerThread2 {
private int countDown = 5;
private Thread t;
public InnerThread2(String name) {
t = new Thread(name) {
public void run() {
try {
while(true) {
System.out.println(this);
if(--countDown == 0) return;
sleep(10);
}
} catch(InterruptedException e) {
System.out.println("sleep() interrupted");
}
}
public String toString() {
return getName() + ": " + countDown;
}
};
t.start();
}
}
class InnerThread2 { private int countDown = 5; private Thread t; public InnerThread2(String name) { t = new Thread(name) { public void run() { try { while(true) { System.out.println(this); if(--countDown == 0) return; sleep(10); } } catch(InterruptedException e) { System.out.println("sleep() interrupted"); } } public String toString() { return getName() + ": " + countDown; } }; t.start(); } }
class InnerThread2 {
    private int countDown = 5;
    private Thread t;
    public InnerThread2(String name) {
        t = new Thread(name) {
            public void run() {
                try {
                    while(true) {
                        System.out.println(this);
                        if(--countDown == 0) return;
                        sleep(10);
                    }
                } catch(InterruptedException e) {
                    System.out.println("sleep() interrupted");
                }
            }
            public String toString() {
                return getName() + ": " + countDown;
            }
        };
        t.start();
    }
}

9.5. 内部类实现Runnable

  下面的InnerRunnable1使用内部类实现Runnable

import java.util.concurrent.TimeUnit;
class InnerRunnable1 {
private int countDown = 5;
private Inner inner;
private class Inner implements Runnable {
Thread t;
Inner(String name) {
t = new Thread(this, name);
t.start();
}
public void run() {
try {
while(true) {
System.out.println(this);
if(--countDown == 0) return;
TimeUnit.MILLISECONDS.sleep(10);
}
} catch(InterruptedException e) {
System.out.println("sleep() interrupted");
}
}
public String toString() {
return t.getName() + ": " + countDown;
}
}
public InnerRunnable1(String name) {
inner = new Inner(name);
}
}
import java.util.concurrent.TimeUnit; class InnerRunnable1 { private int countDown = 5; private Inner inner; private class Inner implements Runnable { Thread t; Inner(String name) { t = new Thread(this, name); t.start(); } public void run() { try { while(true) { System.out.println(this); if(--countDown == 0) return; TimeUnit.MILLISECONDS.sleep(10); } } catch(InterruptedException e) { System.out.println("sleep() interrupted"); } } public String toString() { return t.getName() + ": " + countDown; } } public InnerRunnable1(String name) { inner = new Inner(name); } }
import java.util.concurrent.TimeUnit;

class InnerRunnable1 {
    private int countDown = 5;
    private Inner inner;
    private class Inner implements Runnable {
        Thread t;
        Inner(String name) {
            t = new Thread(this, name);
            t.start();
        }
        public void run() {
            try {
                while(true) {
                    System.out.println(this);
                    if(--countDown == 0) return;
                    TimeUnit.MILLISECONDS.sleep(10);
                }
            } catch(InterruptedException e) {
                System.out.println("sleep() interrupted");
            }
        }
        public String toString() {
            return t.getName() + ": " + countDown;
        }
    }
    public InnerRunnable1(String name) {
        inner = new Inner(name);
    }
}

9.6. 匿名内部类实现Runnable

  下面的InnerRunnable2使用匿名内部类实现Runnable

import java.util.concurrent.TimeUnit;
class InnerRunnable2 {
private int countDown = 5;
private Thread t;
public InnerRunnable2(String name) {
t = new Thread(new Runnable() {
public void run() {
try {
while(true) {
System.out.println(this);
if(--countDown == 0) return;
TimeUnit.MILLISECONDS.sleep(10);
}
} catch(InterruptedException e) {
System.out.println("sleep() interrupted");
}
}
public String toString() {
return Thread.currentThread().getName() + ": " + countDown;
}
}, name);
t.start();
}
}
import java.util.concurrent.TimeUnit; class InnerRunnable2 { private int countDown = 5; private Thread t; public InnerRunnable2(String name) { t = new Thread(new Runnable() { public void run() { try { while(true) { System.out.println(this); if(--countDown == 0) return; TimeUnit.MILLISECONDS.sleep(10); } } catch(InterruptedException e) { System.out.println("sleep() interrupted"); } } public String toString() { return Thread.currentThread().getName() + ": " + countDown; } }, name); t.start(); } }
import java.util.concurrent.TimeUnit;

class InnerRunnable2 {
    private int countDown = 5;
    private Thread t;
    public InnerRunnable2(String name) {
        t = new Thread(new Runnable() {
            public void run() {
                try {
                    while(true) {
                        System.out.println(this);
                        if(--countDown == 0) return;
                        TimeUnit.MILLISECONDS.sleep(10);
                    }
                } catch(InterruptedException e) {
                    System.out.println("sleep() interrupted");
                }
            }
            public String toString() {
                return Thread.currentThread().getName() + ": " + countDown;
            }
        }, name);
        t.start();
    }
}

9.7. 在方法内创建线程

  下面的ThreadMethodrunTask()中创建并并开始线程。如果创建线程只是为了做一些辅助性的工作,这种方式比在构造器中创建并开始线程更加合适。

class ThreadMethod {
private int countDown = 5;
private Thread t;
private String name;
public ThreadMethod(String name) { this.name = name; }
public void runTask() {
if(t == null) {
t = new Thread(name) {
public void run() {
try {
while(true) {
System.out.println(this);
if(--countDown == 0) return;
sleep(10);
}
} catch(InterruptedException e) {
System.out.println("sleep() interrupted");
}
}
public String toString() {
return getName() + ": " + countDown;
}
};
t.start();
}
}
}
class ThreadMethod { private int countDown = 5; private Thread t; private String name; public ThreadMethod(String name) { this.name = name; } public void runTask() { if(t == null) { t = new Thread(name) { public void run() { try { while(true) { System.out.println(this); if(--countDown == 0) return; sleep(10); } } catch(InterruptedException e) { System.out.println("sleep() interrupted"); } } public String toString() { return getName() + ": " + countDown; } }; t.start(); } } }
class ThreadMethod {
    private int countDown = 5;
    private Thread t;
    private String name;
    public ThreadMethod(String name) { this.name = name; }
    public void runTask() {
        if(t == null) {
            t = new Thread(name) {
                public void run() {
                    try {
                        while(true) {
                            System.out.println(this);
                            if(--countDown == 0) return;
                            sleep(10);
                        }
                    } catch(InterruptedException e) {
                        System.out.println("sleep() interrupted");
                    }
                }
                public String toString() {
                    return getName() + ": " + countDown;
                }
            };
            t.start();
        }
    }
}

  9.39.7中的5个例子可以通过如下的方式运行:

public class ThreadVariations {
public static void main(String[] args) {
new InnerThread1("InnerThread1");
new InnerThread2("InnerThread2");
new InnerRunnable1("InnerRunnable1");
new InnerRunnable2("InnerRunnable2");
new ThreadMethod("ThreadMethod").runTask();
}
}
public class ThreadVariations { public static void main(String[] args) { new InnerThread1("InnerThread1"); new InnerThread2("InnerThread2"); new InnerRunnable1("InnerRunnable1"); new InnerRunnable2("InnerRunnable2"); new ThreadMethod("ThreadMethod").runTask(); } }
public class ThreadVariations {
    public static void main(String[] args) {
        new InnerThread1("InnerThread1");
        new InnerThread2("InnerThread2");
        new InnerRunnable1("InnerRunnable1");
        new InnerRunnable2("InnerRunnable2");
        new ThreadMethod("ThreadMethod").runTask();
    }
}

10. Joining a Thread

  在A线程中调用B线程的join()方法,A线程会被挂起,直到B线程完成后(isAlive()false),A线程才会继续运行。可以在调用join()时加上一个表示超时的参数,如果B线程没有在时限内完成,join()就会直接返回,使A线程得以继续运行。interrupt()也可以打断join()

class Sleeper extends Thread {
private int duration;
public Sleeper(String name, int sleepTime) {
super(name);
duration = sleepTime;
start();
}
public void run() {
try {
sleep(duration);
} catch(InterruptedException e) {
System.out.println(getName() + " was interrupted. " +
"isInterrupted(): " + isInterrupted());
return;
}
System.out.println(getName() + " has awakened");
}
}
class Joiner extends Thread {
private Sleeper sleeper;
public Joiner(String name, Sleeper sleeper) {
super(name);
this.sleeper = sleeper;
start();
}
public void run() {
try {
sleeper.join();
} catch(InterruptedException e) {
System.out.println("Interrupted");
}
System.out.println(getName() + " join completed");
}
}
public class Joining {
public static void main(String[] args) {
Sleeper sleepy = new Sleeper("Sleepy", 1500),
grumpy = new Sleeper("Grumpy", 1500);
Joiner dopey = new Joiner("Dopey", sleepy),
doc = new Joiner("Doc", grumpy);
grumpy.interrupt();
}
}
/* Output
Grumpy was interrupted. isInterrupted(): false
Doc join completed
Sleepy has awakened
Dopey join completed
*/
class Sleeper extends Thread { private int duration; public Sleeper(String name, int sleepTime) { super(name); duration = sleepTime; start(); } public void run() { try { sleep(duration); } catch(InterruptedException e) { System.out.println(getName() + " was interrupted. " + "isInterrupted(): " + isInterrupted()); return; } System.out.println(getName() + " has awakened"); } } class Joiner extends Thread { private Sleeper sleeper; public Joiner(String name, Sleeper sleeper) { super(name); this.sleeper = sleeper; start(); } public void run() { try { sleeper.join(); } catch(InterruptedException e) { System.out.println("Interrupted"); } System.out.println(getName() + " join completed"); } } public class Joining { public static void main(String[] args) { Sleeper sleepy = new Sleeper("Sleepy", 1500), grumpy = new Sleeper("Grumpy", 1500); Joiner dopey = new Joiner("Dopey", sleepy), doc = new Joiner("Doc", grumpy); grumpy.interrupt(); } } /* Output Grumpy was interrupted. isInterrupted(): false Doc join completed Sleepy has awakened Dopey join completed */
class Sleeper extends Thread {
    private int duration;
    public Sleeper(String name, int sleepTime) {
        super(name);
        duration = sleepTime;
        start();
    }
    public void run() {
        try {
            sleep(duration);
        } catch(InterruptedException e) {
            System.out.println(getName() + " was interrupted. " + 
                    "isInterrupted(): " + isInterrupted());
            return;
        }
        System.out.println(getName() + " has awakened");
    }
}

class Joiner extends Thread {
    private Sleeper sleeper;
    public Joiner(String name, Sleeper sleeper) {
        super(name);
        this.sleeper = sleeper;
        start();
    }
    public void run() {
        try {
            sleeper.join();
        } catch(InterruptedException e) {
            System.out.println("Interrupted");
        }
        System.out.println(getName() + " join completed");
    }
}

public class Joining {
    public static void main(String[] args) {
        Sleeper sleepy = new Sleeper("Sleepy", 1500),
                grumpy = new Sleeper("Grumpy", 1500);
        Joiner dopey = new Joiner("Dopey", sleepy),
                doc = new Joiner("Doc", grumpy);
        grumpy.interrupt();
    }
}
/* Output
Grumpy was interrupted. isInterrupted(): false
Doc join completed
Sleepy has awakened
Dopey join completed
*/

Sleepersleep()可以被interrupt()打断。interrupt()会设置一个标志位,指示线程被打断,interrupt()产生的异常被捕获后,这个标志位就会被清除。从打印可以看到,Sleeper捕获InterruptedException后,查询isInterrupted()的值为false。之后很快就会出现打印:

Doc join completed
Doc join completed
Doc join completed

因为doc对应的grumpy已经被打断。

  如果不打断grumpy,而是打断doc,则可以看到doc能够被成功打断并立即完成:

public class Joining {
public static void main(String[] args) {
Sleeper sleepy = new Sleeper("Sleepy", 1500),
grumpy = new Sleeper("Grumpy", 1500);
Joiner dopey = new Joiner("Dopey", sleepy),
doc = new Joiner("Doc", grumpy);
doc.interrupt();
}
}
/*
Interrupted
Doc join completed
Grumpy has awakened
Sleepy has awakened
Dopey join completed
*/
public class Joining { public static void main(String[] args) { Sleeper sleepy = new Sleeper("Sleepy", 1500), grumpy = new Sleeper("Grumpy", 1500); Joiner dopey = new Joiner("Dopey", sleepy), doc = new Joiner("Doc", grumpy); doc.interrupt(); } } /* Interrupted Doc join completed Grumpy has awakened Sleepy has awakened Dopey join completed */
public class Joining {
    public static void main(String[] args) {
        Sleeper sleepy = new Sleeper("Sleepy", 1500),
                grumpy = new Sleeper("Grumpy", 1500);
        Joiner dopey = new Joiner("Dopey", sleepy),
                doc = new Joiner("Doc", grumpy);
        doc.interrupt();
    }
}
/*
Interrupted
Doc join completed
Grumpy has awakened
Sleepy has awakened
Dopey join completed
*/

11. Creating Responsive User Interfaces

  多线程的一个重要应用是用来创建响应式的用户界面,通过把耗时的业务放置到单独的线程去运行,前台界面就可以及时地响应用户的交互。

class UnresponsiveUI {
private volatile double d = 1;
public UnresponsiveUI() throws Exception {
while(d > 0)
d = d + (Math.PI + Math.E) / d;
System.in.read(); // Never gets here
}
}
public class ResponsiveUI extends Thread {
private static volatile double d = 1;
public ResponsiveUI() {
setDaemon(true);
start();
}
public void run() {
while(true) {
d = d + (Math.PI + Math.E) / d;
}
}
public static void main(String[] args) throws Exception {
//! new UnresponsiveUI(); // Must kill this process
new ResponsiveUI();
System.in.read();
System.out.println(d); // Shows progress
}
}
class UnresponsiveUI { private volatile double d = 1; public UnresponsiveUI() throws Exception { while(d > 0) d = d + (Math.PI + Math.E) / d; System.in.read(); // Never gets here } } public class ResponsiveUI extends Thread { private static volatile double d = 1; public ResponsiveUI() { setDaemon(true); start(); } public void run() { while(true) { d = d + (Math.PI + Math.E) / d; } } public static void main(String[] args) throws Exception { //! new UnresponsiveUI(); // Must kill this process new ResponsiveUI(); System.in.read(); System.out.println(d); // Shows progress } }
class UnresponsiveUI {
    private volatile double d = 1;
    public UnresponsiveUI() throws Exception {
        while(d > 0)
            d = d + (Math.PI + Math.E) / d;
        System.in.read(); // Never gets here 
    }
}

public class ResponsiveUI extends Thread {
    private static volatile double d = 1;
    public ResponsiveUI() {
        setDaemon(true);
        start();
    }
    public void run() {
        while(true) {
            d = d + (Math.PI + Math.E) / d;
        }
    }
    public static void main(String[] args) throws Exception {
        //! new UnresponsiveUI(); // Must kill this process 
        new ResponsiveUI();
        System.in.read();
        System.out.println(d); // Shows progress 
    }
}

12. Thread Groups

  线程组持有线程的集合,这里直接引用Thinking in Java的原话:

The value of thread groups can be summed up by a quote from Joshua Bloch, the software architect who, while he was at Sun, fixed and greatly improved the Java collections library in JDK 1.2 (among other contributions):

“Thread groups are best viewed as an unsuccessful experiment, and you may simply
ignore their existence.”

13. Catching Exceptions

  异常不会跨线程传递,如果有未被处理的异常从run()中逃了出来,该异常会被传递到控制台。在Java SE 5之前需要使用线程组来捕获异常,而在Java SE 5中则可以使用Executors,不必直接接触线程组。

  下面的代码在run()中抛出异常:

import java.util.concurrent.*;
public class ExceptionThread implements Runnable {
public void run() {
throw new RuntimeException();
}
public static void main(String[] args) {
ExecutorService exec = Executors.newCachedThreadPool();
exec.execute(new ExceptionThread());
}
}
import java.util.concurrent.*; public class ExceptionThread implements Runnable { public void run() { throw new RuntimeException(); } public static void main(String[] args) { ExecutorService exec = Executors.newCachedThreadPool(); exec.execute(new ExceptionThread()); } }
import java.util.concurrent.*;

public class ExceptionThread implements Runnable {
    public void run() {
        throw new RuntimeException();
    }
    public static void main(String[] args) {
        ExecutorService exec = Executors.newCachedThreadPool();
        exec.execute(new ExceptionThread());
    }
}

该异常未被处理,会在控制台打印出来:

Exception in thread "pool-1-thread-1" java.lang.RuntimeException
Exception in thread "pool-1-thread-1" java.lang.RuntimeException
Exception in thread "pool-1-thread-1" java.lang.RuntimeException

  直接在main()中加上try-catch,依然无法捕获异常:

public class NaiveExceptionHandling {
public static void main(String[] args) {
try {
ExecutorService exec = Executors.newCachedThreadPool();
exec.execute(new ExceptionThread());
} catch(RuntimeException ue) {
// This statement will NOT execute!
System.out.println("Exception has been handled!");
}
}
}
public class NaiveExceptionHandling { public static void main(String[] args) { try { ExecutorService exec = Executors.newCachedThreadPool(); exec.execute(new ExceptionThread()); } catch(RuntimeException ue) { // This statement will NOT execute! System.out.println("Exception has been handled!"); } } }
public class NaiveExceptionHandling {
    public static void main(String[] args) {
        try {
            ExecutorService exec = Executors.newCachedThreadPool();
            exec.execute(new ExceptionThread());
        } catch(RuntimeException ue) {
            // This statement will NOT execute!
            System.out.println("Exception has been handled!");
        }
    }
}

同样的异常仍旧会在控制台上打印出来:

Exception in thread "pool-1-thread-1" java.lang.RuntimeException
Exception in thread "pool-1-thread-1" java.lang.RuntimeException
Exception in thread "pool-1-thread-1" java.lang.RuntimeException

  为了解决这个问题,需要改变Executor产生线程的方式。Java SE 5中新加入的Thread.UncaughtExceptionHandler接口允许为Thread对象添加异常处理程序。当线程中出现未捕获的异常、即将终止时,Thread.UncaughtExceptionHandler.uncaughtException()会被自动调用。

  这里使用自定义ThreadFactory的方式,为其生成的线程附加上Thread.UncaughtExceptionHandler,然后把ThreadFactory传递给Executors,得到ExecutorService

import java.util.concurrent.*;
class ExceptionThread2 implements Runnable {
public void run() {
Thread t = Thread.currentThread();
System.out.println("run() by " + t);
System.out.println("eh = " + t.getUncaughtExceptionHandler());
throw new RuntimeException();
}
}
class MyUncaughtExceptionHandler implements
Thread.UncaughtExceptionHandler {
public void uncaughtException(Thread t, Throwable e) {
System.out.println("caught " + e);
}
}
class HandlerThreadFactory implements ThreadFactory {
public Thread newThread(Runnable r) {
System.out.println(this + " creating new Thread");
Thread t = new Thread(r);
System.out.println("created " + t);
t.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
System.out.println("eh = " + t.getUncaughtExceptionHandler());
return t;
}
}
public class CaptureUncaughtException {
public static void main(String[] args) {
ExecutorService exec = Executors.newCachedThreadPool(
new HandlerThreadFactory());
exec.execute(new ExceptionThread2());
}
}
/* Output
com.company.exceptions.HandlerThreadFactory@7f31245a creating new Thread
created Thread[Thread-0,5,main]
eh = com.company.exceptions.MyUncaughtExceptionHandler@6d6f6e28
run() by Thread[Thread-0,5,main]
eh = com.company.exceptions.MyUncaughtExceptionHandler@6d6f6e28
com.company.exceptions.HandlerThreadFactory@7f31245a creating new Thread
created Thread[Thread-1,5,main]
eh = com.company.exceptions.MyUncaughtExceptionHandler@c6f6863
caught java.lang.RuntimeException
*/
import java.util.concurrent.*; class ExceptionThread2 implements Runnable { public void run() { Thread t = Thread.currentThread(); System.out.println("run() by " + t); System.out.println("eh = " + t.getUncaughtExceptionHandler()); throw new RuntimeException(); } } class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler { public void uncaughtException(Thread t, Throwable e) { System.out.println("caught " + e); } } class HandlerThreadFactory implements ThreadFactory { public Thread newThread(Runnable r) { System.out.println(this + " creating new Thread"); Thread t = new Thread(r); System.out.println("created " + t); t.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler()); System.out.println("eh = " + t.getUncaughtExceptionHandler()); return t; } } public class CaptureUncaughtException { public static void main(String[] args) { ExecutorService exec = Executors.newCachedThreadPool( new HandlerThreadFactory()); exec.execute(new ExceptionThread2()); } } /* Output com.company.exceptions.HandlerThreadFactory@7f31245a creating new Thread created Thread[Thread-0,5,main] eh = com.company.exceptions.MyUncaughtExceptionHandler@6d6f6e28 run() by Thread[Thread-0,5,main] eh = com.company.exceptions.MyUncaughtExceptionHandler@6d6f6e28 com.company.exceptions.HandlerThreadFactory@7f31245a creating new Thread created Thread[Thread-1,5,main] eh = com.company.exceptions.MyUncaughtExceptionHandler@c6f6863 caught java.lang.RuntimeException */
import java.util.concurrent.*;

class ExceptionThread2 implements Runnable {
    public void run() {
        Thread t = Thread.currentThread();
        System.out.println("run() by " + t);
        System.out.println("eh = " + t.getUncaughtExceptionHandler());
        throw new RuntimeException();
    }
}

class MyUncaughtExceptionHandler implements
        Thread.UncaughtExceptionHandler {
    public void uncaughtException(Thread t, Throwable e) {
        System.out.println("caught " + e);
    }
}

class HandlerThreadFactory implements ThreadFactory {
    public Thread newThread(Runnable r) {
        System.out.println(this + " creating new Thread");
        Thread t = new Thread(r);
        System.out.println("created " + t);
        t.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
        System.out.println("eh = " + t.getUncaughtExceptionHandler());
        return t;
    }
}

public class CaptureUncaughtException {
    public static void main(String[] args) {
        ExecutorService exec = Executors.newCachedThreadPool(
                new HandlerThreadFactory());
        exec.execute(new ExceptionThread2());
    }
}
/* Output
com.company.exceptions.HandlerThreadFactory@7f31245a creating new Thread
created Thread[Thread-0,5,main]
eh = com.company.exceptions.MyUncaughtExceptionHandler@6d6f6e28
run() by Thread[Thread-0,5,main]
eh = com.company.exceptions.MyUncaughtExceptionHandler@6d6f6e28
com.company.exceptions.HandlerThreadFactory@7f31245a creating new Thread
created Thread[Thread-1,5,main]
eh = com.company.exceptions.MyUncaughtExceptionHandler@c6f6863
caught java.lang.RuntimeException
*/

MyUncaughtExceptionHandler实现了Thread.UncaughtExceptionHandler,这里只是将异常打印出来。HandlerThreadFactory通过ThreadsetUncaughtExceptionHandler(),为Thread加上MyUncaughtExceptionHandler用来处理异常。从打印可见,ExceptionThread2抛出的未捕获的异常被MyUncaughtExceptionHandler处理了。

  可以通过Thread的setDefaultUncaughtExceptionHandler()方法为Thread添加默认的异常处理:

import java.util.concurrent.*;
public class SettingDefaultHandler {
public static void main(String[] args) {
Thread.setDefaultUncaughtExceptionHandler(
new MyUncaughtExceptionHandler());
ExecutorService exec = Executors.newCachedThreadPool();
exec.execute(new ExceptionThread());
}
}
/* Output
caught java.lang.RuntimeException
*/
import java.util.concurrent.*; public class SettingDefaultHandler { public static void main(String[] args) { Thread.setDefaultUncaughtExceptionHandler( new MyUncaughtExceptionHandler()); ExecutorService exec = Executors.newCachedThreadPool(); exec.execute(new ExceptionThread()); } } /* Output caught java.lang.RuntimeException */
import java.util.concurrent.*;

public class SettingDefaultHandler {
    public static void main(String[] args) {
        Thread.setDefaultUncaughtExceptionHandler(
                new MyUncaughtExceptionHandler());
        ExecutorService exec = Executors.newCachedThreadPool();
        exec.execute(new ExceptionThread());
    }
}
/* Output
caught java.lang.RuntimeException
*/

如果没有为线程单独设置UncaughtExceptionHandler,则会应用默认的UncaughtExceptionHandler(如果有的话)。