关于Thread的一些tips。
处理未捕捉的异常
在JVM运行时,当一个线程抛出一个运行时异常,JVM是怎么处理这个异常的,遇到这种情况,这种“漏网”的异常又该怎么正确处理。我们可以通过实现UncaghtExceptionHandler接口,处理用户未catch,且逃脱到JVM的异常。首先声明UncaghtExceptionHandler的实现类,将该Handler注册到程序中:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32import java.lang.Thread.UncaughtExceptionHandler;
public class TestUncaughtExceptionHandler {
static class MyUncaughtExceptionHandler implements UncaughtExceptionHandler {
public void uncaughtException(Thread t, Throwable e) {
System.out.printf("Thead: name[%s], status[%s] \n", t.getName(), t.getState());
System.out.printf("Exception: [%s] \n", e.getMessage());
}
}
static class ExceptionThread extends Thread {
public ExceptionThread(String name) {
super(name);
}
public void run() {
System.out.printf("Thread [%s] is running. \n", getName());
throw new RuntimeException("Man made exception for " + getName() + ".");
}
}
public static void main(String[] args) {
Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
for(int i=0; i<6; i++) {
new ExceptionThread("sama" + i).start();
}
}
}
以上通过
Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler());向程序中注册默认的未捕捉异常处理Handler,其运行结果如下,可以看到在MyUncaughtExceptionHandler中对未手动catch到的异常信息进行处理。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 main thread terminated.
Thread [sama0] is running.
Thead: name[sama0], status[RUNNABLE]
Exception: [Man made exception for sama0.]
Thread [sama5] is running.
Thead: name[sama5], status[RUNNABLE]
Exception: [Man made exception for sama5.]
Thread [sama4] is running.
Thead: name[sama4], status[RUNNABLE]
Exception: [Man made exception for sama4.]
Thread [sama3] is running.
Thread [sama2] is running.
Thead: name[sama2], status[RUNNABLE]
Exception: [Man made exception for sama2.]
Thread [sama1] is running.
Thead: name[sama3], status[RUNNABLE]
Exception: [Man made exception for sama3.]
Thead: name[sama1], status[RUNNABLE]
Exception: [Man made exception for sama1.]
What is a shutdown hook?
“终止钩子”是什么,终止钩子是在JVM终止运行时会执行的一个线程,它可以通过获取运行时环境的addShutdownHook(Thread)添加,如:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44public class TestShutdownHook {
static class SleepThread extends Thread {
public void run() {
try {
Thread.sleep(5000);
System.out.println("Sleep thread execute over.");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Runtime runtime = Runtime.getRuntime();
runtime.addShutdownHook(new Thread() {
public void run() {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("The first shutdown hook executing.");
}
});
runtime.addShutdownHook(new Thread() {
public void run() {
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("The second shutdown hook executing.");
}
});
new SleepThread().start();
System.out.println("main thread execute over.");
}
}
运行结果为:
1
2
3
4 main thread execute over.
Sleep thread execute over.
The first shutdown hook executing.
The second shutdown hook executing.
如果将第一个hook thread的休眠时间设的比第二个长,则执行结果为:
1
2
3
4 main thread execute over.
Sleep thread execute over.
The second shutdown hook executing.
The first shutdown hook executing.
因为
Runtime.addShutdownHook(Thread)中,是调用ApplicationShutdownHooks.add(Thread)将Hook Thread添加到IdentityHashMap<Thread, Thread> hooks中,而执行终止钩子线程的方法为:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 static void runHooks() {
Collection<Thread> threads;
synchronized(ApplicationShutdownHooks.class) {
threads = hooks.keySet();
hooks = null;
}
for (Thread hook : threads) {
hook.start();
}
for (Thread hook : threads) {
try {
hook.join();
} catch (InterruptedException x) { }
}
}
是将所有的终结钩子线程一起启动,然后通过
join()等待他们都执行完成,runHooks()才结束。
Thread & Runnable
继承Thread或实现Runnable并将实现传入Thread都可以声明一个线程,不同的是,调用Runnable的run()方法是不会起一个线程:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19public class TestThreadRunnable {
public static void main(String[] args) {
Thread t1 = new Thread("thread01") {
public void run() {
System.out.println("this is thread01. name: " + Thread.currentThread().getName());
}
};
Thread t2 = new Thread("thread02") {
public void run() {
System.out.println("this is thread02. name: " + Thread.currentThread().getName());
}
};
t1.start();
t2.run();
}
}
它的执行结果如下,可知在调用Runnable的run()方法,在run()方法体内获取到的当前线程还是主线程 main,所以正确的启动一个线程的方法是调用Thread的start()方法。1
2this is thread01. name: thread01
this is thread02. name: main