热门搜索 :
考研考公
您的当前位置:首页正文

java高并发-Thread.start()的执行流程

来源:东饰资讯网

本来讲完《静态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

关注公众号,找我不迷路哦。

微信公众号信息
Top