【亿码酷站-编程开发教程】收集全网优质教程及源码资源!
全网优质软件开发、平面设计等教程及精品源码资源一站可得,www.ymkuzhan.com!
细品 Java 中启动线程的正确和错误方式
前文回顾
- 详细分析 Java 中实现多线程的方法有几种?(从本质上出发)
start 方法和 run 方法的比较
代码演示:
/** * <p> * start() 和 run() 的比较 * </p> * * @author 踏雪彡寻梅 * @version 1.0 * @date 2020/9/20 - 16:15 * @since JDK1.8 */public class StartAndRunMethod { public static void main(String[] args) { // run 方法演示 // 输出: name: main // 说明由主线程去执行的, 不符合新建一个线程的本意 Runnable runnable = () -> { System.out.println("name: " + Thread.currentThread().getName()); }; runnable.run(); // start 方法演示 // 输出: name: Thread-0 // 说明新建了一个线程, 符合本意 new Thread(runnable).start(); } }复制代码
从以上示例可以分析出以下两点:
-
直接使用
run
方法不会启动一个新线程。(错误方式) -
start
方法会启动一个新线程。(正确方式)
start 方法分析
start 方法的含义以及注意事项
-
start
方法可以启动一个新线程。- 线程对象在初始化之后调用了
start
方法之后, 当前线程(通常是主线程)会请求 JVM 虚拟机如果有空闲的话来启动一下这边的这个新线程。 - 也就是说, 启动一个新线程的本质就是请求 JVM 来运行这个线程。
- 至于这个线程何时能够运行,并不是简单的由我们能够决定的,而是由线程调度器去决定的。
- 如果它很忙,即使我们运行了
start
方法,也不一定能够立刻的启动线程。 - 所以说
srtart
方法调用之后,并不意味这个方法已经开始运行了。它可能稍后才会运行,也很有可能很长时间都不会运行,比如说遇到了饥饿的情况。 - 这也就印证了有些情况下,线程 1 先掉用了
start
方法,而线程 2 后调用了start
方法,却发现线程 2 先执行线程 1 后执行的情况。 - 总结: 调用
start
方法的顺序并不能决定真正线程执行的顺序。 - 注意事项
start
方法会牵扯到两个线程。- 第一个就是主线程,因为我们必须要有一个主线程或者是其他的线程(哪怕不是主线程)来执行这个
start
方法,第二个才是新的线程。 - 很多情况下会忽略掉为我们创建线程的这个主线程,不要误以为调用了
start
就已经是子线程去执行了,这个语句其实是主线程或者说是父线程来执行的,被执行之后才去创建新线程。
- 线程对象在初始化之后调用了
-
start
方法创建新线程的准备工作- 首先,它会让自己处于就绪状态。
- 就绪状态指已经获取到除了 CPU 以外的其他资源, 如已经设置了上下文、栈、线程状态以及 PC(PC 是一个寄存器,PC 指向程序运行的位置) 等。
- 做完这些准备工作之后,就万事俱备只欠东风了,东风就是 CPU 资源。
- 做完准备工作之后,线程才能被 JVM 或操作系统进一步去调度到执行状态等待获取 CPU 资源,然后才会真正地进入到运行状态执行
run
方法中的代码。
- 首先,它会让自己处于就绪状态。
-
需要注意: 不能重复的执行 start 方法
-
代码示例
/** * <p> * 演示不能重复的执行 start 方法(两次及以上), 否则会报错 * </p> * * @author 踏雪彡寻梅 * @version 1.0 * @date 2020/9/20 - 16:47 * @since JDK1.8 */public class CantStartTwice { public static void main(String[] args) { Runnable runnable = () -> { System.out.println("name: " + Thread.currentThread().getName()); }; Thread thread = new Thread(runnable); // 输出: name: Thread-0 thread.start(); // 输出: 抛出 java.lang.IllegalThreadStateException // 即非法线程状态异常(线程状态不符合规定) thread.start(); } }复制代码
-
报错的原因
start
一旦开始执行,线程状态就从最开始的 New 状态进入到后续的状态,比如说 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 方法 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 */ } } }复制代码
源码中的流程
第一步:启动新线程时会首先检查线程状态是否为初始状态, 这也是以上抛出异常的原因。即以下代码:
if (threadStatus != 0) throw new IllegalThreadStateException();复制代码
其中 threadStatus
这个变量的注释如下,也就是说 Java 的线程状态最初始(还没有启动)的时候表示为 0:
/* Java thread status for tools, * initialized to indicate thread 'not yet started' */private volatile int threadStatus = 0;复制代码
第二步:将其加入线程组。即以下代码:
group.add(this);复制代码
第三步:最后调用 start0()
这个 native 方法(native 代表它的代码不是由 Java 实现的,而是由 C/C++ 实现的,具体实现可以在 JDK 里面看到,了解即可), 即以下代码:
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 */ } }复制代码
run 方法分析
run 方法源码分析
@Overridepublic void run() { // 传入了 target 对象(即 Runnable 接口的实现), 执行传入的 target 对象的 run 方法 if (target != null) { target.run(); } }复制代码
对于 run 方法的两种情况
-
第一种: 重写了
Thread
类的run
方法,Thread
的run
方法会失效, 将会执行重写的run
方法。 -
第二种: 传入了
target
对象(即Runnable
接口的实现),执行Thread
的原有run
方法然后接着执行target
对象的run
方法。 -
总结:
run
方法就是一个普通的方法, 上文中直接去执行run
方法也就是相当于我们执行自己写的普通方法一样,所以它的执行线程就是我们的主线程。- 所以要想真正的启动线程,不能直接调用
run
方法,而是要调用start
方法,其中可以间接的调用run
方法。
细品 Java 中启动线程的正确和错误方式
—–文章转载自PHP中文网如有侵权请联系admin#tyuanma.cn删除
电脑的安全模式是什么意思?
转载请注明来源:细品 Java 中启动线程的正确和错误方式_亿码酷站_编程开发技术教程
本文永久链接地址:https://www.ymkuzhan.com/1270.html
本文永久链接地址:https://www.ymkuzhan.com/1270.html
下载声明:
本站资源如无特殊说明默认解压密码为www.ymkuzhan.com建议使用WinRAR解压; 本站资源来源于用户分享、互换、购买以及网络收集等渠道,本站不提供任何技术服务及有偿服务,资源仅提供给大家学习研究请勿作它用。 赞助本站仅为维持服务器日常运行并非购买程序及源码费用因此不提供任何技术支持,如果你喜欢该程序,请购买正版! 版权声明:
下载本站资源学习研究的默认同意本站【版权声明】若本站提供的资源侵犯到你的权益,请提交版权证明文件至邮箱ymkuzhan#126.com(将#替换为@)站长将会在三个工作日内为您删除。 免责声明:
您好,本站所有资源(包括但不限于:源码、素材、工具、字体、图像、模板等)均为用户分享、互换、购买以及网络收集而来,并未取得原始权利人授权,因此禁止一切商用行为,仅可用于个人研究学习使用。请务必于下载后24小时内彻底删除,一切因下载人使用所引起的法律相关责任,包括但不限于:侵权,索赔,法律责任,刑事责任等相关责任,全部由下载人/使用人,全部承担。以上说明,一经发布视为您已全部阅读,理解、同意以上内容,如对以上内容持有异议,请勿下载,谢谢配合!支持正版,人人有责,如不慎对您的合法权益构成侵犯,请联系我们对相应内容进行删除,谢谢!