当前位置: 首页 > 编程日记 > 正文

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

f30165be8f7640540aadd8e1460db350.png

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

10f54e9a15d4c763c5988beed435b051.png

相关文章:

Concurrency Runtime in Visual C++ 2010

PDC 2010 Hejlsberg的演讲中我们看到了VB.NET、C#新的简化异步编程的方式(可以下载新的Async CTP体验)。之前的TPL(Task Parallel Library)简化了并行编程。工业语言的飞速发展大大改进、简化了开发人员的编程方式。不仅是微软平台…

关于安卓你不知道的6件事

安卓第一次亮相是出如今2008年公布的HTC Dream手机上,到如今为止它已经6岁了。或许没有人想过在2010年底它就成为了智能手机平台率先的操作系统。这当然要感谢谷歌的努力和强大的財力支持。尽管眼下安卓系统是世界上最流行的移动操作系统,可是关于它的非…

vmware的三种网络模式讲解

vmware有三种网络设置模式,分别是Bridged(桥接),NAT(网络地址转换),Host-only(私有网络共享主机) 1.Bridged(桥接) 桥接模式默认使用的是:VMnet0 什么是桥接模式?桥接模式就是把主机网卡和虚拟机虚拟的网卡利用虚拟网桥进行通信。…

当前路径_[JSP] 07 JSP 路径问题

首先先明确一下下列URL的假设一个URL是这样的losthost:8080/myservlet/path服务器根路径:losthost:8080/项目根路径:losthost:8080/myservlet/明确了服务器根路径和项目根路径之后,我们就可以开始学习后面的知识了servlet路径问题请求转发和重定向的相对路径写法总结:Servlet重…

CPU时间戳获取

inline long long timt(){long long p; int&a*(((int*)&p)1);__asm__ __volatile__("rdtsc":"a"(p),"d"(a));return p; } 因为在64位CPU上rdtsc出来的结果仍然是在%eax和%edx,而%rax却不是由%eax和%edx拼起来的(反正我试了它没用...也许…

NYOJ 366 D的小L

地址&#xff1a;http://acm.nyist.net/JudgeOnline/problem.php?pid366 方法&#xff1b;用next_permutation&#xff08;pɝmjʊteʃə&#xff09;来求解&#xff0c;递归调用代码 1 #include<stdio.h>2 #include<algorithm>3 using namespace std;4 int main(…

MySQL 语句整理 2019-5-3

MySQL 语句整理 在整理完Oracle的一些常见用语句后,由于MySQL的语法跟Oracle略有不同,随跟PN的MySQL视频进行了间接整理. 查询薪水大于1800, 并且部门编号为20或30的员工sql select deptno,ename,sal from emp where sal > 1800 and (deptno 20 or deptno 30); and 优先级…

联想e580没有声音_现在你可以购买通过 Linux 认证的联想 ThinkPad 和 ThinkStation

曾经有一段时间&#xff0c;ThinkPad 是 Linux 用户的首选系统。但那是在 ThinkPad 还是 IBM 的产品的时候。来源&#xff1a;https://linux.cn/article-12283-1.html作者&#xff1a;Abhishek Prakash译者&#xff1a;Xingyu.Wang曾经有一段时间&#xff0c; ThinkPad 是 Linu…

linux平台下防火墙iptables原理(转)

原文地址&#xff1a;http://www.cnblogs.com/ggjucheng/archive/2012/08/19/2646466.html iptables简介 netfilter/iptables&#xff08;简称为iptables&#xff09;组成Linux平台下的包过滤防火墙&#xff0c;与大多数的Linux软件一样&#xff0c;这个包过滤防火墙是免费的&a…

SQL语法大全

SQL语句大全 --语 句 功 能--数据操作SELECT --从数据库表中检索数据行和列INSERT --向数据库表添加新数据行DELETE --从数据库表中删除数据行UPDATE --更新数据库表中的数据--数据定义CREATE TABLE --创建一个数据库表DROP TABLE --从数据库中删除表ALTER TABLE --修改数据库表…

Node.js的url模块简介

什么是URL URL是Uniform Location Resource的缩写&#xff0c;翻译为“统一资源定位符”&#xff0c;也就是描述资源位置的固定表示方法。被URL描述的资源可以位于互联网上&#xff0c;也可以位于本地。 URL的组成结构 基本URL包含模式&#xff08;或者成为协议&#xff09;&am…

python游戏结束显示分数代码_当游戏循环在Python中运行时,多线程来显示游戏分数?...

我想根据比赛时间的推移得分。为此&#xff0c;我想让两个循环同时运行。游戏循环和得分循环&#xff0c;每1.5秒加1。当我运行程序时&#xff0c;分数不会出现。我是否正确使用多线程&#xff1f;这是最好的办法吗&#xff1f;为了简单起见&#xff0c;我只发布了相关代码。谢…

NOIP2015解题报告 By ljt12138

Day1t1 幻方 练过的一道题&#xff0c;简单模拟&#xff0c;用二维数组存储&#xff0c;ij两个游标记录横纵坐标&#xff0c;利用题目条件改变坐标直到填入n个数即可。复杂度O(n^2) AC Day2t2 图的最小环 首先抽象出图论模型。每个人对应点&#xff0c;传输对应边。因为自己…

设计模式笔记——Adapter

适配器模式Adapter 将一个类的接口转换成用户希望的另外一个接口。适配器模式主要应用于希望复用一些现存的类&#xff0c;但是接口又与复用的环境要求不一样的情况。 package adapter.pattern; /*** * author Real H LI**/ public class Existed {public void OldRequest(){Sy…

iOS12-crash错误-reason: 'UITableView failed to obtain a cell from its dataSource'

错误原因&#xff1a; cellForRowAtIndexPath函数返回了nil override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {let cell tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: in…

with as python_python - with as的用法

摘自&#xff1a;http://www.cnblogs.com/DswCnblog/p/6126588.htmlWith 是什么&#xff1f;有一些任务可能事先需要设置&#xff0c;事后做清理工作。对于这种场景&#xff0c;Python的with语句提供了一种非常方便的处理方式。一个很好的例子是文件处理&#xff0c;你需要获取…

wcf系列学习5天速成——第四天 wcf之分布式架构(转载)

今天是wcf系列的第四天&#xff0c;也该出手压轴戏了。嗯&#xff0c;现在的大型架构&#xff0c;都是神马的&#xff0c; nginx鸡群&#xff0c;iis鸡群&#xff0c;wcf鸡群&#xff0c;DB鸡群&#xff0c;由一个人作战变成了群殴....... 今天我就分享下wcf鸡群&#xff0c;高…

检测jQuery.js是否已加载的判断代码

转载自http://www.jb51.net/article/27185.htm 测类、方法、变量或属性是否已存在&#xff0c;这是Javascript编程基础知识。在这里我们就是要检测jQuery()或$()函数是否存在 当然&#xff0c;该方法不局限于jQuery的检测&#xff0c;对与任何Javascript变量或函数都是通用的。…

Java中常见的锁简述

在Java的应用中&#xff0c;或多或少的都会接触到一些锁&#xff0c;那么问题就来了&#xff0c;在Java中&#xff0c;常见的锁有哪些&#xff0c;都有什么样的作用&#xff1f;&#xff1f; 这里给大家简单的简述一下这些学常见的锁。 本文件所涉及到的锁&#xff1a; 1.公平锁…

加了好友怎么还掉血_微信聊天窗口出现风险提醒,无法添加好友解决办法

再更&#xff0c;你想马上解封&#xff0c;你得有朋友。没有朋友&#xff0c;你就等个十天半个月&#xff01;更新一下 帖子浏览人挺多的&#xff0c;有问题可以在评论里相互交流&#xff0c;下面是原文&#xff1a;先让我说一句脏话&#xff1a;傻x微信风控系统 终于特么的聊天…

[小明爬坑系列]AssetBundle原理介绍

一.简介 Assetbundle 是Unity Pro提供提供的功能&#xff0c;它可以把多个游戏对象或者资源二进制文件封装到Assetbundle中&#xff0c;提供了封装与解包的方法使用起来很便利。 二.移动平台特点 Assetbundle是可以把预制,文件以及场景都打包到Assetbundle中去的,但是在移动平台…

Ubuntu16.04菜单栏侧边栏不显示

2019独角兽企业重金招聘Python工程师标准>>> &#xff08;&#xff11;&#xff09;只有桌面上显示一些原有的文档或图片。但打开文档或者图片后也是没有窗口菜单栏&#xff0c;这样连关闭都没法点&#xff0c;快捷&#xff21;ltF4也没有反应。而且鼠标点击拖动不了…

C 语言中的 strtok 调用小技巧

1 #include <stdio.h>2 #include <string.h>3 4 char *my_strtok(char *buf, char *delims)5 {6 static int first 1;7 if(first){ //8 first 0; // 互斥操作&#xff0c;确保后面代码仅在本次调用执行9 return strtok(buf, delims); 10…

powershell连接数据库_PowerShell 连接SQL Server 数据库

PowerShell 通过ADO.NET连接SQL Server数据库&#xff0c;并执行SQL脚本。工作中整理的一小段脚本&#xff0c;后来没有用上&#xff0c;先记录在这里&#xff1a;建立数据库连接查询返回一个DataTatble对象执行一条SQL语句通过事物执行多条SQL语句## 建立数据库连接.#function…

GDB调试进阶

GDB 调试 ------------------------------------------------------------------------------- gdb 帮助文档 help -- 查看 gdb 的命令种类 help <CmdType> -- 查看 CmdType 种类的 gdb 命令 apropos <keyWord> -- 查看关键字 keyWord 的相关命令 info <keyWord…

RubyGems 库发现了后门版本的网站开发工具 bootstrap-sass

安全研究人员在官方的 RubyGems 库发现了后门版本的网站开发工具 bootstrap-sass。该工具的下载量高达 2800 万次&#xff0c;但这并不意味着下载的所有版本都存在后门&#xff0c;受影响的版本是 v3.2.0.3&#xff0c;研究人员呼吁用户尽可能快的更新&#xff0c;认为可能有数…

读大叔深入理解javascript(2)

Module模式的应用&#xff1a; var Caculator function(eqt){var box document.getElementById(eqt);return {add:function(x,y){return xy;}} }; var a new Caculator(11); a.add(1,3); // return 4 这种方式每次都需要new 一下&#xff0c;也就是说每个实例在内存里都是一…

android 无法接收广播_别告诉我你不认识Android中广播接收者(二)

前面我们了解了什么是广播接收者与广播接收者的创建&#xff0c;这一次我们要接着继续去了解广播接收者的相关知识&#xff0c;这些知识包括广播接收者的注册、自定义广播与广播的类型。当我们学习完广播接收者之后&#xff0c;该如何才能让它起到作用呢&#xff1f;还有广播接…

jQuery中$(function(){})与(function($){})(jQuery)的区别

首先&#xff0c;这两个函数都是在页面载入后执行的函数&#xff0c;其中两者的区别在于&#xff1a; 在jQuery中$(function(){})等同于jQuery(function(){})&#xff0c;另一个写法为jQuery(document).ready(function(){ })&#xff0c;在DOM加载完成之后立即执行&#xff0c;…

UITableView注意点

在iOS应用中&#xff0c;UITableView应该是使用率最高的视图之一了。iPod、时钟、日历、备忘录、Mail、天气、照片、电话、短信、 Safari、App Store、iTunes、Game Center⋯几乎所有自带的应用中都能看到它的身影&#xff0c;可见它的重要性。然而在使用第三方应用时&#xff…