JAVA线程问题,线程启动失败

  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的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: