本来讲完《静态synchronized方法和非静态synchronized方法是否存在竟态》这篇之后,想给大家讲一下“主线程怎么捕获子线程”的异常的。但所谓“知其然,然后知其所以然”,我们这次想讲一下“线程的运行机制”,过程中你就会明白
在不做特殊处理的时候,为什么主线无法捕获子线程的异常
Runnable和Thread
大家都知道,想要实现线程有两种方式。实现Runnable和继承Thread类,至于他们的优缺点这里不做赘述。我们需要关注的是作为线程,最为核心的方法是run()。对于run()方法,java doc给出的解释是在线程启动时,会调用run()方法。而线程启动调用的是Thread类中的start()方法.
start()方法是主线程调用的方法,用来启动子线程的;run()方法是子线程的行为方法
我们来看一段代码
public class ExceptionThread extends Thread {
@Override
public void run() throws RuntimeException{
System.out.println("这是run的方法"+Thread.currentThread().getName());
int i = 1/0;
}
@Override
public synchronized void start() throws RuntimeException{
System.out.println("这是start的方法"+Thread.currentThread().getName());
}
}
ExceptionThread继承了Thread类,并重写了start()和 run()方法,并在 run()方法中抛出一个异常
public class ThreadTest {
public static void main(String[] args) {
ExceptionThread exceptionThread = new ExceptionThread();
try{
exceptionThread.start();
System.out.println("我是主线程,我还能执行吗?");
}catch (RuntimeException r){
System.out.println("这是我抛出的异常"+r.getMessage());
}
}
}
在main方法中,我们创建了ExceptionThread线程,并调用start()来启动线程。
我们来看一下运行结果
这是start的方法main
我是主线程,我还能执行吗?
可以看到,线程ExceptionThread的start()方法是被main调用的,而run()方法并没有执行
这是为什么呢?为什么我们启动了线程,却没有执行run()方法呢?
我们说到,start()方法是被main线程调用的,他之所以可以启动线程,是因为在start()中调用了Thread.start()。在我们的代码中,我们重写了Thread.start()方法,并且在start()中没有调用super.start()方法。所以,这个时候,start()仅仅看作是一个普通方法被main线程调用。
我们修改一下start()方法
public class ExceptionThread extends Thread {
@Override
public void run() throws RuntimeException{
System.out.println("这是run的方法"+Thread.currentThread().getName());
int i = 1/0;
}
@Override
public synchronized void start() throws RuntimeException{
super.start();
System.out.println("这是start的方法"+Thread.currentThread().getName());
}
}
我们在ExceptionThread的start()方法中调用super.start()看看会发生什么
这是start的方法main
我是主线程,我还能执行吗?
这是run的方法Thread-0
Exception in thread "Thread-0" java.lang.ArithmeticException: / by zero
at thread.catch_exception.ExceptionThread.run(ExceptionThread.java:14)
可以看到,ExceptionThread中的run()被线程Thread-0调用了
这又是为什么呢?
start()源码解析
public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
}
}
}
这个是Thread.start()源码。从代码中我们可以看到一个start0(),这个方法是要JVM调用的,是c语言。他的用途在于创建新的线程,并调用Thread.run()方法
@Override
public void run() {
if (target != null) {
target.run();
}
}
run()方法会调用target.run(),这个target就是创建的ExceptionThread实例,在这儿之后就会调用ExceptionThread.run()了
@Override
public void run() throws RuntimeException{
System.out.println("这是run的方法"+Thread.currentThread().getName());
int i = 1/0;
}
所以,想要启动线程,必须要调用,Thread.start()方法,之后再调用Thread.run()方法,再之后才是ExceptionThread.run()方法
子线程的异常为什么不能抛给主线程
上文我们说到,子线程的代码都是在run()方法中完成的,而run()方法必须要通过Thread.run()方法调用,但是在Thread.run()方法中并没有抛出异常,即没有throws Exception,所以子线程的异常不会被主线程捕获。
//定义线程
public class ExceptionThread extends Thread {
@Override
public void run() throws RuntimeException{
System.out.println("这是run的方法"+Thread.currentThread().getName());
int i = 1/0;
}
@Override
public synchronized void start() throws RuntimeException{
super.start();
System.out.println("这是start的方法"+Thread.currentThread().getName());
}
}
//执行线程,并尝试捕获子线程的异常
public class ThreadTest {
public static void main(String[] args) {
ExceptionThread exceptionThread = new ExceptionThread();
try{
exceptionThread.start();
System.out.println("我是主线程,我还能执行吗?");
}catch (RuntimeException r){
System.out.println("这是我抛出的异常"+r.getMessage());
}
}
}
输出结果
这是start的方法main
这是run的方法Thread-0
我是主线程,我还能执行吗?
Exception in thread "Thread-0" java.lang.ArithmeticException: / by zero
at thread.catch_exception.ExceptionThread.run(ExceptionThread.java:14)
我们看到,主线程并没有捕获子线程的异常
image.png我的同名公众号“阿拉丁节能灯”会同步【简书】上的文章,风格比较【艳丽】哈哈哈,如下:
关注公众号,找我不迷路哦。
微信公众号信息