并发基础之正确启动多线程

start方法和run方法比较

很多人包括我自己也以前也认为run和start方法都可以启动一个线程,并且两者的功能都差不多,然后在学习的过程中认识到这是错误的,他们之间是截然不同的。先来看一段演示代码:

/**
 * @author Chen
 * @Description start()和run()方法比较
 * @create 2019-11-05 22:01
 */
public class StartAndRunMethod {
    public static void main(String[] args) {
        //使用run方法
        Runnable runnable = ()-> System.out.println(Thread.currentThread().getName());
        runnable.run();

        //使用start方法
        new Thread(runnable).start();
    }
}

输出结果:

main
Thread-0

可以看到,执行run方法的线程是主线程,而执行start方法的才是一个新的子线程。

start方法解读

我们都知道start方法就是启动一个新线程,其实真是情况是start方法执行过程中会涉及到两个线程,一个是主线程,另一个是新的子线程,当调用start方法是,子线程并不会马上启动起来,子线程会获取除了cpu以外的一些资源,进入就状态,等待CPU的调用。至于什么时候调用,这个就不是我们所能决定的了,而是线程调度器来决定的。所以有一些先运行start方法的线程可能会在比后调用start方法的线程启动的慢。

当我们重复调用两次start方法会发生什么?

会抛出一个异常IllegalThreadStateException(非法的线程状态),其实线程一旦执行,就会进入runnable状态,一旦执行完毕,就会进入结束状态,不会在返回回去。

接下来我们在来仔细看看start(),了解一下它执行的过程中到底干了什么。

  public synchronized void start() {
        /**
         * This method is not invoked for the main method thread or "system"
         * group threads created/set up by the VM. Any new functionality added
         * to this method in the future may have to also be added to the VM.
         *
         * A zero status value corresponds to state "NEW".
         */
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        /* Notify the group that this thread is about to be started
         * so that it can be added to the group's list of threads
         * and the group's unstarted count can be decremented. */
        group.add(this);

        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }

start()中,首先会检查线程的状态,判断threadStatus是不是等于0,也就是判断是不是一个新启动的线程,这是执行两次start()抛出异常的原因。(在线程刚刚被初始化的时候默认的状态为0)

然后又做了一个加入线程组这个行为,然后又执行了个start0的native方法。

run方法解读

其实run方法比较简单,只有三行代码:如果target也就是传入的接口不为空,就执行重写的run方法的内容,否则就执行传入的run方法的内容。

    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }

所以当我们执行run方法的时候,它就会作为一个普通的方法,和我们自己写的一样,所以控制台打印出来的是一个主线程。

所以我们想启动一个新线程不能直接调用run方法,而是应该调用start方法来间接调用run方法。

猜你喜欢

转载自www.cnblogs.com/chen88/p/11802279.html