细品 Java 中启动线程的正确和错误方式

发布时间:2023-09-16 点击:106
细品 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.threadsta

.org域名是什么意思,支持备案吗?
腾讯汤道生:数字优先 生态联动 让数据要素成为“新能源”
做SEO西部数码站排名要稳定 不要做以下几点
域名什么意思?域名有什么特点?
主机流量问题-其他问题
upupoo激活码2022免费领取 upupoo怎么跳过激活码
网站标题如要要进行SEO优化组合要怎么做呢
免费企业建站,需要注意这几个方面