4-1 启动线程的方式
启动线程的正确方式:start()
一、start()有哪些作用?
1,启动新线程
线程对象在初始化之后被Main线程调用了start(),于是当前线程(Main)就会告诉JVM来执行一个新的线程。start()做的最重要的一件事情就是通知我们的JVM在有空闲的情况来开启一个新的线程。所以启动一个新线程的本质就是去请求JVM来运行我们这个新的线程,至于这个新线程何时能被执行,并不是由我们能决定的,是由线程调度器决定的,如果它很忙,即使我们已经调用了start(),它也未必能够马上启动,甚至可能要过好久才会执行。我们调用start()的顺序并不能决定线程执行的先后顺序。
2,start()其实会让两个线程同时运行
第一个就是我们的父线程,因为我们必须要有一个父线程来执行start(),第二个才是我们刚刚创建的那个子线程,很多i情况下我们会忽略掉为我们创建线程调用start()的父线程,new Thread().start()被我们的父线程执行之后才会去创建子线程,
3,start()做了哪些准备工作?
我们的子线程其实是需要做一些准备工作,才能让它正常运行。
首先,它会让自己处于就绪状态(Runnable),所谓的就绪状态就是指我已经获取导了除了CPU以外的其他资源。比如说我已经设置了上下文,栈,线程状态以及PC(寄存器,指向我们程序运行的位置),做完了以上准备工作,就只欠CPU资源了。在做完准备工作之后我们的线程才可以被我们的JVM或操作系统进一步去调度到执行状态,调度到执行状态,等待获取CPU资源,然后才会真正进入到运行状态,执行我们run()中的逻辑。
4,不能重复地执行start()
否则会抛出IllegalThreadStateException
一旦线程开始执行就从NEW状态变为其他状态,一旦执行完毕就编程TERMINATED状态,而且不会再返回了。
二、start()源码解析
1,启动新线程检查线程状态
2,加入线程组
3,调用start0()
是一个native,每个JDK版本,jvm版本对native方法的实现或许是不一样的。
启动线程的错误方式:run()
如果直接调用Runnable.run(),会在当前线程执行Runnable任务。如果要启动新线程执行Runnable,还是要通过Thread去调用Runnable的run().
CAFE BABE
三、问题
1,一个线程两次调用start()会出现什么情况?
第二次会报错,因为调用start()的时候,检查线程状态,如果不是NEW就会抛出IllegalThreadStateException
2,既然start()会调用run(),为什么我们选择调用start(),而不是直接调用run()呢?
直接调用run()是默认在当前线程运行run().而不是在新建的线程中执行