集团主站
欢迎来到成都达内官方网站!达内—美国上市公司 亿元级外企IT培训企业!
成都it培训哪家好
成都it培训哪家好
全国服务监督电话:400-111-8989  |   联系客服   |
当前位置:主页 > 培训课程 > Java >

[成都java培训机构哪家好]Java中启动线程的正确和错误方式

发布者: 成都达内     浏览次数:     发布时间:2020-09-29 16:46:58

[成都java培训机构哪家好]直接使用 run 方法不会启动一个新线程。(错误方式),start 方法会启动一个新线程。(正确方式)。...

  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 方法源码分析

  @Override

  public 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培训班
    免费试听名额发放中...
  • 成都C++培训班
    免费试听名额发放中...
  • 成都PHP培训班
    免费试听名额发放中...
  • 成都网络工程培训班
    免费试听名额发放中...
  • 成都Unity3D培训班
    免费试听名额发放中...
  • 成都大数据培训班
    免费试听名额发放中...
  • 成都uid培训班
    免费试听名额发放中...
  • 成都会计培训班
    免费试听名额发放中...
  • 成都Python培训班
    免费试听名额发放中...
  • 成都嵌入式培训班
    免费试听名额发放中...
  • 成都web培训班
    免费试听名额发放中...
  • 成都软件测试培训班
    免费试听名额发放中...
在线留言
提交

校区地址:成都市锦江区东大街紫东楼端35号明宇金融广场19楼1906室

联系电话:400-111-8989

公交路线:芷泉街(18路;21路;43路;48路;104路;152路;335路 ) 地铁路线:东门大桥站(地铁2号线)

校区地址:成都市高新区奥克斯广场蜀锦路209号一楼商铺

联系电话:400-111-8989

公交路线:益州大道锦城大道口(18路;21路;43路;48路;104路;152路;335路 ) 地铁路线:孵化园(地铁1号线)

校区地址:成都锦江区东大街芷泉街229号东方广场C座3楼303

联系电话:400-111-8989

公交路线:芷泉街(188路;115路;515路;236路;505路;501路;84路 ) 地铁路线:东门大桥站(地铁2号线)

校区地址:成都市武侯区佳灵路3号红牌楼广场2号写字楼11楼1115号

联系电话:400-111-8989

公交路线:红牌楼东(11路;92路;100路;111路;139路;g28路;快速公交K1/K2) 地铁路线:红牌楼站(地铁3号线)

校区地址:成都市锦江区红星路二段70号四川日报大厦502-2

联系电话:400-111-8989

公交路线:市二医院站(6路;49路;102路;5路;37路;g92路;) 地铁路线:地铁市二医院(地铁3号线)

校区地址:成都市锦江区东大街芷泉段229号东方广场C座16层

联系电话:400-111-8989

公交路线:芷泉街(18路;21路;43路;48路;104路;152路;335路 ) 地铁路线:东门大桥站(地铁2号线)

校区地址:四川省成都市武侯区高新科技孵化园9号园区E座7楼

联系电话:400-111-8989

公交路线:益州大道锦城大道口(18路;21路;43路;48路;104路;152路;335路 ) 地铁路线:孵化园(地铁1号线)

了解达内动态
关注成都达内教育公众号

首页 | 关于达内 | 课程中心 | 专家师资 | 视频教程 | 学员空间 | 校企合作 | 新闻资讯 | 就业指导 | 网站地图

2016-2025 达内时代科技集团有限公司 版权所有 京ICP证8000853号-56 蜀ICP备18021046号-3