怎样才是正确的线程启动方式?

启动线程的正确和错误方式

start()和run()的比较

public class StartAndRunMethod {

    public static void main(String[] args) {

        Runnable runnable = () -> {
            System.out.println(Thread.currentThread().getName());

        };
        runnable.run();

        new Thread(runnable).start();
    }
}

打印结果:

main
Thread-0

我们会发现,runnable.run()方法是由main线程执行的,而要子线程执行就一定要先调用start()启动新线程去执行。

start()方法原理解读

  • 方法含义
  1. 启动新线程
  2. 准备工作
  3. 不能重复调用start
public class CantStartTwice {

    public static void main(String[] args) {
        Thread thread = new Thread();
        thread.start();
        thread.start();
    }
}

结果:

Exception in thread "main" java.lang.IllegalThreadStateException
	at java.lang.Thread.start(Thread.java:705)
	at com.hd.thread.start.CantStartTwice.main(CantStartTwice.java:14)

抛出非法的线程异常。

  • 源码解析
  1. 启动新线程检查线程状态
  2. 加入线程组
  3. 调用start0
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(); //调用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 */
            }
        }
    }
  • 深入JDK源码
  1. start0()是一个本地方法

run()方法原理解读

  • 源码解析
  • 两种情况
@Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }

面试问题

1.一个线程两次调用start方法会出现什么情况?为什么?

会抛出非法线程状态异常,在start()方法里面,一开始就会进行启动新线程检查线程状态

2.既然start方法会调用run方法,为什么我们选择调用start方法,而不是直接调用run方法呢?

因为调用start方法才是真正意义上的启动一个线程,它会去经历线程的各个生命周期,如果调用run方法,它仅仅是一个普通方法而已,不会用子线程调用。

3.start方法的执行流程是什么?

  1. 检查线程状态,只有NEW状态下的线程才能继续,否则会抛出IllegalThreadStateException异常(在运行中或者已结束的线程,都不能再次启动)。
  2. 被加入线程组。
  3. 调用start0方法启动线程。
  4. 注意点:
    • start方法是被synchronized修饰的方法,可以保证线程安全;
    • 由JVM创建的main方法线程和system组线程,并不会通过start来启动。

笔记来源:慕课网悟空老师视频《Java并发核心知识体系精讲》

发布了112 篇原创文章 · 获赞 303 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_36221788/article/details/102633991