JAVA线程问题,线程启动失败
细品 Java 中启动线程的正确和错误方式
前文回顾
Java中实现多线程的方法有几种?(本质上)
start 方法和 run 方法的比较
代码演示:
/**
* p
* start()和run()的比较
* /p
*
* @作者寻梅穿越雪地
* @版本1.0
* @日期2020/9/20 - 16:15
* @从JDK1.8开始
*/public class startandrammethod { public static void main(string[]args){//运行方法演示
//输出:名称:main
//说明主线程执行的东西不符合创建新线程的初衷。
Runnable runnable=() - {
name: Thread.currentThread()。getName());
};
runnable . run();//开始方法演示
//输出:名称:线程-0
//说明新建了一个线程,符合初衷。
新线程(可运行)。start();
}
}从上面的例子复制代码,可以分析出以下两点:
直接使用run方法不会启动新线程。(错误的方式)
start方法启动一个新线程。(正确的方式)
start 方法分析
start 方法的含义以及注意事项
start方法可以启动一个新线程。
在对象初始化后调用start方法后,当前线程(通常是主线程)会请求JVM虚拟机在这里启动这个新线程,如果它是空闲的。也就是说,启动一个新线程的本质就是请求JVM运行这个线程。至于这个线程什么时候能运行,不是简单的由我们决定,而是由线程调度器决定。如果它很忙,即使我们运行start方法,也可能无法立即启动线程。所以srtart方法被调用后,并不意味着这个方法已经开始运行。可能要晚一点才运行,也可能运行不了很久,比如在饥饿的情况下。这证明了在某些情况下,线程1先调用start方法,而线程2后调用start方法,才发现线程2先执行线程1再执行。总结:调用start方法的顺序并不能决定真正线程执行的顺序。注意事项启动方法将涉及两个线程。第一个是主线程,因为我们必须有一个主线程或者其他线程(即使不是主线程)来执行这个start方法,第二个是新线程。很多情况下,为我们创建线程的主线程会被忽略。不要把start的调用误认为是子线程的执行。这个语句实际上是由主线程或者父线程执行的,新线程只有在执行完之后才会创建。Start方法创建一个新线程。
首先,它会让自己做好准备。就绪状态是指已经获取了CPU之外的其他资源,比如设置上下文、堆栈、线程状态、PC(PC是寄存器,指向程序运行的位置)。这些准备都做好了,万事俱备,只欠东风,就是CPU资源。准备工作完成后,线程可以被JVM或操作系统进一步调度到执行状态等待CPU资源,然后它会真正进入运行状态执行run方法中的代码。需要注意: 不能重复的执行 start 方法
代码示例
/**
* p
*演示start方法不能重复(两次或更多),否则将报告错误。
* /p
*
* @作者寻梅穿越雪地
* @版本1.0
* @日期2020/9/20 - 16:47
* @从JDK1.8开始
*/public class cantstartwice { public static void main(String[]args){
Runnable runnable=() - {
name: Thread.currentThread()。getName());
};
Thread thread=新线程(runnable);//输出:名称:线程-0
thread . start();//输出:抛出Java . lang . illegalthreadstateexception。
//即非法线程状态异常(线程状态不符合规定)
thread . start();
}
}复制代码报告错误的原因
一旦执行start,线程状态将从初始的新状态进入后续状态,比如Runnable。那么一旦线程结束,线程就会变成终止状态,而终止状态是永远无法返回的,所以会抛出上面的异常,也就是说无法返回初始状态。这里的描述不够清晰。为了更透彻的理解,我们来看看源代码。
start 方法源码分析
源码
公共同步void start() { /**
*主方法线程或“系统”不会调用此方法
*由虚拟机创建/设置的组线程。添加了任何新功能
*将来可能还需要将此方法添加到VM中。
*
*零状态值对应于状态“新”。
*/
//第一步是检查线程状态是否为初始状态,这也是上面抛出异常的原因。
if (threadStatus!=0)抛出新的IllegalThreadStateException();/*通知小组该线程即将启动
*以便将其添加到组的线程列表中
*并且该组的未启动计数可以递减。*/
//第二步,加入线程组
group . add(this);boolean started=falseTry {//第三步,调用start0方法。
start 0();
开始=真;
}最后{试试{ if(!开始){
group . thread start failed(this);
}
} catch (Throwable ignore) { /*什么都不做。如果start0抛出了Throwable,那么
它将在调用堆栈中向上传递*/
}
}
}复制代码
源码中的流程
第一步:当启动一个新的线程时,它会先检查线程状态是否为初始状态,这也是抛出上面异常的原因。以下代码:
if (threadStatus!=0)抛出新的IllegalThreadStateException();复制代码。变量threadStatus的注释如下,也就是说Java的线程状态在初始化时(尚未启动)表示为0:
/*工具的Java线程状态,
*初始化以指示线程“尚未启动”
*/private volatile int thread status=0;复制代码第二步:,将其添加到线程组。以下代码:
group . add(this);复制代码第三步:最后调用native方法start0 () (native是指其代码不是用Java实现的,而是用C/C实现的,具体实现可以看JDK,知道就行),即下面这段代码:
boolean started=falseTry {//第三步,调用start0方法。
start 0();
开始=真;
}最后{试试{ if(!开始){
group . thread start failed(this);
}
} catch (Throwable ignore) { /*什么都不做。如果start0抛出了Throwable,那么
它将在调用堆栈中向上传递*/
}
}复制代码
run 方法分析
run 方法源码分析
@ override public void run(){//传入目标对象(即Runnable接口的实现),执行传入目标对象的run方法。
如果(目标!=null) {
target . run();
}
}复制代码
对于 run 方法的两种情况
第一种方法:重写Thread类的run方法,将Thread的run方法作废,执行重写后的run方法。
第二:传入目标对象(即Runnable接口的实现),执行Thread原来的run方法,然后执行目标对象的run方法。
总结:
run方法是一个普通的方法。在前面的文章中,直接执行run方法相当于执行一个我们自己编写的普通方法,所以它的执行线程就是我们的主线程。所以,要想真正启动线程,应该调用start方法,而不是直接调用run方法,其中可以间接调用run方法。这些是Java中启动线程的正确和错误方式的细节。请多关注我们的其他相关文章!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。