start 方法和 run 方法的比较

代码演示:

/** * 


 * start() 和 run() 的比较
 *


 *
 * @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 方法,第二个才是新的线程。

很多情况下会忽略掉为我们创建线程的这个主线程,不要误以为调用了 start 就已经是子线程去执行了,这个语句其实是主线程或者说是父线程来执行的,被执行之后才去创建新线程。

线程对象在初始化之后调用了 start 方法之后, 当前线程(通常是主线程)会请求 JVM 虚拟机如果有空闲的话来启动一下这边的这个新线程。

也就是说, 启动一个新线程的本质就是请求 JVM 来运行这个线程。

至于这个线程何时能够运行,并不是简单的由我们能够决定的,而是由线程调度器去决定的。

如果它很忙,即使我们运行了 start 方法,也不一定能够立刻的启动线程。

所以说 srtart 方法调用之后,并不意味这个方法已经开始运行了。它可能稍后才会运行,也很有可能很长时间都不会运行,比如说遇到了饥饿的情况。

这也就印证了有些情况下,线程 1 先掉用了 start 方法,而线程 2 后调用了 start 方法,却发现线程 2 先执行线程 1 后执行的情况。

总结: 调用 start 方法的顺序并不能决定真正线程执行的顺序。

注意事项

start 方法会牵扯到两个线程。

第一个就是主线程,因为我们必须要有一个主线程或者是其他的线程(哪怕不是主线程)来执行这个 start 方法,第二个才是新的线程。

很多情况下会忽略掉为我们创建线程的这个主线程,不要误以为调用了 start 就已经是子线程去执行了,这个语句其实是主线程或者说是父线程来执行的,被执行之后才去创建新线程。

需要注意: 不能重复的执行 start 方法

代码示例

/*** 


* 演示不能重复的执行 start 方法(两次及以上), 否则会报错
*


*
* @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' */

第二步:

将其加入线程组。即以下代码:

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 方法,Threadrun 方法会失效, 将会执行重写的 run 方法。

第二种: 传入了 target 对象(即 Runnable 接口的实现),执行 Thread 的原有 run 方法然后接着执行 target 对象的 run 方法。

总结:

run 方法就是一个普通的方法, 上文中直接去执行 run 方法也就是相当于我们执行自己写的普通方法一样,所以它的执行线程就是我们的主线程。

所以要想真正的启动线程,不能直接调用 run 方法,而是要调用 start 方法,其中可以间接的调用 run 方法。

原文链接:cnblogs.com/txxunmei/p/13747631.html

线程组多次调用_详细分析 Java 中启动线程的正确和错误方式相关推荐

  1. 线程启动语句的顺序是否决定线程的执行次序。_详细分析 Java 中启动线程的正确和错误方式

    start 方法和 run 方法的比较 代码演示: /** * * start() 和 run() 的比较 * * * @author 踏雪彡寻梅 * @version 1.0 * @date 202 ...

  2. 不允许使用java方式启动_细品 Java 中启动线程的正确和错误方式

    细品 Java 中启动线程的正确和错误方式 前文回顾详细分析 Java 中实现多线程的方法有几种?(从本质上出发) start 方法和 run 方法的比较 代码演示:/** * * start() 和 ...

  3. java sleep唤醒_详解Java中的线程让步yield()与线程休眠sleep()方法

    Java中的线程让步会让线程让出优先级,而休眠则会让线程进入阻塞状态等待被唤醒,这里我们对比线程等待的wait()方法,来详解Java中的线程让步yield()与线程休眠sleep()方法 线程让步: ...

  4. java lam表达式_详细分析Java Lambda表达式

    在了解Lambda表达式之前我们先来区分一下面向对象的思想和函数式编程思想的区别 面向对象的思想: 做一件事情,找一个能解决这个事情的对象,调用他的方法来解决 函数时编程思想: 只要能获取到结果,谁去 ...

  5. java 判断数组已经存满_详解Java中数组判断元素存在几种方式比较

    1. 通过将数组转换成List,然后使用List中的contains进行判断其是否存在 public static boolean useList(String[] arr,String contai ...

  6. java捕获定时器抛出的异常_详细了解Java中定时器Timer的使用及缺陷分析

    在需要定时并且周期执行任务时,在最初的JAVA工具类库中,Timer可以实现任务的定时周期执行的需求,不过有一定的缺陷,比如,Timer是基于绝对时间而非相对时间,因此Timer对系统时钟比较敏感,本 ...

  7. java 线程等待_代码分析Java中线程的等待与唤醒

    我们先来看一下实例代码: class ThreadA extends Thread{ public ThreadA(String name) { super(name); } public void ...

  8. java线程怎么重启_如何在Java中启动/停止/重启线程?

    10 个答案: 答案 0 :(得分:41) 一旦线程停止,您就无法重新启动它.但是,没有什么可以阻止您创建和启动新线程. 选项1:创建一个新线程,而不是尝试重新启动. 选项2:而不是让线程停止,让它等 ...

  9. 详细分析Java中的浅克隆和深克隆

    本文对浅克隆和深克隆的两种方法(不引入别的开源工具)进行了简单的代码实现(没有内部类语法),对比了浅克隆和深克隆对引用类型的影响,暂不考虑不可变类,确保初学Java者能够看懂并学会,可直接复制源代码进 ...

最新文章

  1. Binder相关面试总结(五):为什么Activity间传递对象需要序列化
  2. dubbo-provider-deploy
  3. 如何和女生聊天不进入友谊区
  4. yum php fpm5.6,CentOS 7 yum 安装php5.6
  5. [调试]Asp.Net常见问题
  6. 1065. 单身狗(25)
  7. 【转】日志记录库(log4cxx)使用指南
  8. a标签里面设置onclick_实现a标签中的各种点击(onclick)事件的方法
  9. 特征工程之特征预处理
  10. poj 3071 Football 概率dp
  11. python程序员专用壁纸_程序员如何一键“Get”高清壁纸?
  12. QT-700多种实用好看的图标,直接拿来用
  13. 基于MODIS的锡林郭勒植被变化监测(附练习数据下载)
  14. 4本建模必读的书籍,每天学一点,获益匪浅
  15. 惊呆了!C语言也能画小猪佩奇?【附源码】社会我佩奇哥!
  16. CREATE TABLESPACE命令详解
  17. 低端交换机环路检测专题
  18. 基于有源钳位三电平的有源电力滤波器(ANPC-APF)MATLAB仿真,包括自建的DSOGI锁相模块和PQ谐波检测模块
  19. 基于51单片机的红外循迹小车
  20. 计算机主机有哪些软胶,小型台式机推荐介绍

热门文章

  1. 微信小程序实现滑动tab切换和点击tab切换并显示相应的数据(附源代码)
  2. iOS 生成带 logo 的二维码,区域截屏保存至相册(小功能二连发 (一))
  3. php.ini 中开启短标签
  4. 安装hadoop图文
  5. Java读取Properties配置文件
  6. Cross-validation
  7. C++简单使用Jsoncpp来读取写入json文件
  8. 完整中英文世界国家级联下拉列表插件【前端版】
  9. SQL2K数据库开发二之查看和修改Sample数据库
  10. 在ARC环境中autoreleasepool(runloop)的研究