Java多线程(Thread)详解之启动与中断

在我的前一篇博客中直接介绍了Thread的”五种“打开方式:Thread的”五种“打开方式icon-default.png?t=N7T8https://blog.csdn.net/qq_45875349/article/details/132644717?spm=1001.2014.3001.5501

但是还没有详细的对Thread类进行说明,这篇博客主要对Thread类进行介绍,然后分享一下自己对start()interrupt()的理解。

目录

1 Thread的常见构造方法

 2 Thread的几个常见属性

3 启动一个线程-start()

4 中断一个线程

4.1 通过共享的标记来进行沟通

4.2 调用 interrupt() 方法来通知

4.3 不同方法下观察Thread标志位是否被重置


1 Thread的常见构造方法

方法     说明
Thread()
创建线程对象
Thread(Runnable target) 使用 Runnable 对象创建线程对象
Thread(String name) 创建线程对象,并命名
Thread(Runnable target, String name) 使用 Runnable 对象创建线程对象,并命名
Thread t1 = new Thread();
Thread t2 = new Thread(new MyRunnable());
Thread t3 = new Thread("这是我的名字");
Thread t4 = new Thread(new MyRunnable(), "这是我的名字");

说明:t1\t2\t3\t4只是线程的变量名而不是真正意义的线程的名字,t只是代码中对于thread的引用,thread的真正名字是指的是Thread类中的name属性的值。我们可以通过jconsole工具来查看通过Thread(String name) 构造方法是不是成功对thread进行了“命名”。

 2 Thread的几个常见属性

属性获取方法
ID
getId()
名称 getName()
状态 getState()
优先级 getPriority()
是否后台线程 isDaemon()
是否存活 isAlive()
是否被中断

isInterrupted() 

ID (getId): getId方法返回线程的唯一标识符,通常作为一个long。

long id = thread.getId();
System.out.println(id);

名称 (getName): getName方法用于获取线程的名称,可以设置线程的名称以便于标识和调试。

String name = thread.getName();
System.out.println(name);

状态 (getState): getState方法用于获取线程的当前状态,通常返回一个Thread.State枚举值,表示线程状态。常见的状态包括 NEWRUNNABLEBLOCKEDWAITINGTIMED_WAITINGTERMINATED。具体的内容会在后面继续深入讲解。

Thread.State threadState = thread.getState();
System.out.println(threadState);

优先级 (getPriority): getPriority方法返回线程的优先级,用于表示线程的相对重要性。线程的优先级通常在1到10之间,其中1表示最低优先级,10表示最高优先级。理论上可以干预线程调度,但是实际线程的调度有很多复杂的因素糅合在一起决定,不一定按照我们自己的预期去执行。

        int threadPriority = thread.getPriority();System.out.println(threadPriority);

是否后台线程 (isDaemon): isDaemon方法用于检查线程是否为后台线程(守护进程)。后台线程在所有非后台线程结束时自动终止。默认情况下,线程是非后台的。

boolean isDaemonThread = thread.isDaemon();

是否存活 (isAlive): isAlive方法检查线程是否还在运行。如果线程已经终止或还没有启动,它将返回false,否则返回true

boolean isThreadAlive = thread.isAlive();
public class ThreadUse {public static void main(String[] args) {Thread thread = new Thread(() -> {for (int i = 0; i < 3; i++) {System.out.println("我在工作..");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}},"t1");thread.start();long id = thread.getId();System.out.println(id);String name = thread.getName();System.out.println(name);Thread.State threadState = thread.getState();System.out.println("status:" + threadState);int threadPriority = thread.getPriority();System.out.println(threadPriority);System.out.println("isDeamon:"+thread.isDaemon());System.out.println("isAlive:" + thread.isAlive());try {Thread.sleep(8000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("isAlive:" + thread.isAlive());System.out.println("status:" + threadState);}
}

3 启动一个线程-start()

在上一篇博客中我们已经看到了如何通过覆写 run 方法创建一个线程对象, 但线程对象被创建出来并不意味着线程就开始运行了。
  • 覆写 run 方法是提供给线程要做的事情的指令清单
  • 线程对象可以认为是张三(主线程)把 李四、王五(新线程)叫过来了
  • 而调用 start() 方法,就是喊一声:行动起来!“,线程才真正独立去执行了(才真的在操作系统的底层创建出一个线程.)。

4 中断一个线程

举一个例子,比如我正在打游戏(执行线程)现在我的妈妈喊我去吃饭(通知中断线程),但是我不一定就立马去吃饭(中断线程),我有可能打一会游戏之后就去吃饭,也有可能直接忽视喊我吃饭一直玩下去。编程中的中断也需要类似的业务那么需要怎么实现呢?怎么通知线程中断呢?这就涉及到停止线程的方式了。

目前通知线程中断常用的方法有两种:

  • 1. 通过共享的标记来进行沟通
  • 2. 调用 interrupt() 方法来通知

4.1 通过共享的标记来进行沟通

示例1:使用自定义的变量作为标志位。需要给标志位加上volatile关键字。

why使用volatile:

在多线程编程中,如果多个线程需要共享一个变量来进行沟通、协作或同步操作,那么需要特别注意线程间的可见性问题。这是因为不同的线程可能在不同的 CPU 缓存中缓存了相同的变量,导致一个线程修改了变量的值,但其他线程可能不会立即看到这个变化,从而导致不一致的行为。

volatile 关键字用于解决变量的可见性问题。当一个变量被声明为 volatile 时,Java 内存模型确保每次读取这个变量时都会直接从主内存中获取最新的值,每次修改这个变量时都会立即将新值写入主内存,而不会使用线程的本地缓存。这就确保了变量的修改对其他线程是可见的。

在使用共享标志位来进行线程协作或同步时,通常需要将这个标志位声明为 volatile,以确保任何一个线程对标志位的修改都能够被其他线程立即看到。

public class Thread_interrupt2 {private static volatile boolean flag = false;public static void main(String[] args) {Thread thread = new Thread(() -> {while (!flag) {System.out.println("打游戏...");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}});thread.start();try {Thread.sleep(2*1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("要吃饭了,别打游戏了!");flag = true;}
}

结果:

4.2 调用 interrupt() 方法来通知

示例 -2 : 使用 Thread.interrupted() 或者 Thread.currentThread().isInterrupted() 代替自定义标志位 .
Thread 内部包含了一个 boolean 类型的变量作为线程是否被中断的标记 . 就像刚才我们自己写的代码一样。
public static void main(String[] args) {Thread working = new Thread() {@Overridepublic void run() {while (!Thread.currentThread().isInterrupted()) {System.out.println("working");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();//在中断前处理的业务System.out.println("做工作交接...");}}}};working.start();//让线程工作5秒try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}//通知线程该结束了System.out.println("通知线程可以结束工作了!");working.interrupt();}
}

这样就可以让线程停止下来吗??很遗憾并不是,看看执行的结果:

为什么会出现这样的结果呢?

在Java中,使用thread.interrupt()方法来通知线程中断的行为是正确的,但线程是否真正中断取决于线程本身的实现以及它的中断状态如何被检查。interrupt()方法仅仅是设置了线程的中断状态,它并不会强制停止线程执行。线程需要自己在适当的时候检查中断状态并决定是否终止执行。

当线程处于sleep状态时,如果在另一个线程中调用了thread.interrupt()来中断正在sleep的线程,会导致sleep方法抛出InterruptedException异常。但是,这并不会立即停止线程的执行,而是会将中断状态重置为false,同时抛出异常。线程需要捕获这个异常并决定是否终止执行。

 也就是说interrupt()方法会导致sleep方法抛出InterruptedException异常, 在抛出异常的同时还会将中断状态重置为false,所以导致了上面的现象。而我们调用interrupt()方法是想将中断状态置为true的,所以最后没有达到我们预期的效果。

既然如此那么该怎么达到最终的中断效果呢?在上面的代码中我们可以在捕获异常后使用return或者break跳出循环即可。在使用return或者break语句之前可以做一些最后的逻辑。

public class Thread_interrupt {public static void main(String[] args) {Thread working = new Thread() {@Overridepublic void run() {while (!Thread.currentThread().isInterrupted()) {System.out.println("working");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();//在中断前处理的业务System.out.println("做工作交接...");//结束任务了System.out.println("结束工作");return;}}}};working.start();//让线程工作5秒try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}//通知线程该结束了System.out.println("通知线程可以结束工作了!");working.interrupt();}
}

 4.3 不同方法下观察Thread标志位是否被重置

现在出现了三个容易混淆的方法:

方法说明
public void interrupt()
中断对象关联的线程,如果线程正在阻塞,则以异常方式通知同时重置标志位
public static boolean interrupted()
判断当前线程的中断标志位是true还是false,调用后重置标志位为false
public boolean isInterrupted()
判断对象关联的线程的标志位true还是false,调用后不重置标志位

 thread收到通知的方式有两种:

1. 如果线程因为调用 wait/join/sleep 等方法而阻塞挂起,则以 InterruptedException 异常的形式通
知, 重置中断标志
        当出现 InterruptedException 的时候 , 要不要结束线程取决于 catch 中代码的写法 . 可以选择
        忽略这个异常, 也可以跳出循环结束线程(上面代码以及展示了)
2. 否则,只是内部的一个中断标志被设置, thread 可以通过
  • Thread.interrupted() 判断当前线程的中断标志的设置,重置中断标志为false
public class ThreadFlag {public static void main(String[] args) {Thread thread = new Thread(() -> {for (int i = 0; i < 10; i++) {System.out.println(Thread.interrupted());}});thread.start();//通知中断 此时对应第二钟情况 没有因为调用 wait/join/sleep 等方法而阻塞挂起thread.interrupt();}
}

 运行结果:

  • Thread.currentThread().isInterrupted() 判断指定线程的中断标志被设置,不重置中断标志这种方式通知收到的更及时,即使线程正在 sleep 也可以马上收到。
public class ThreadFlag {public static void main(String[] args) {Thread thread = new Thread(() -> {for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().isInterrupted());}});thread.start();//通知中断 此时对应第二钟情况 没有因为调用 wait/join/sleep 等方法而阻塞挂起thread.interrupt();}
}

运行结果:

 然后我们就可以通过这个中断标记来自由实现线程具体是否中断的业务了。

到这里对于interrupt是不是豁然开朗了!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/121935.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

全局指令和局部指令

自定义v-load <template><div class"main"><div class"box" v-loading"isLoading"><ul><li v-for"item in list" :key"item.id" class"news"><div class"left">…

【网络编程】TCP/IP协议(互联网的基石)

(꒪ꇴ꒪ )&#xff0c;Hello我是祐言QAQ我的博客主页&#xff1a;C/C语言&#xff0c;数据结构&#xff0c;Linux基础&#xff0c;ARM开发板&#xff0c;网络编程等领域UP&#x1f30d;快上&#x1f698;&#xff0c;一起学习&#xff0c;让我们成为一个强大的攻城狮&#xff0…

实时操作系统Freertos开坑学习笔记:(四):临界段保护、列表与列表项

前言 废话不多说&#xff0c;直接看主要要探究的问题&#xff1a; 一、临界段代码保护 1.什么是临界段&#xff1f; 图里面说&#xff0c;临界区的代码是不能被打断的&#xff0c;它运行时不能被中断打断&#xff0c;也不能由于非阻塞任务延时而切换到其他任务去。 比如说…

bash: cmake: command not found...+++++++lsb_release: command not found

一 .bash: cmake: command not found… centos中安装那个cmake。 1、问题 [rootPC3 home]# cmake bash: cmake: command not found... Similar command is: make当前系统&#xff1a; [rootPC3 home]# lsb_release -a LSB Version: :core-4.1-amd64:core-4.1-noarch:cxx…

蝶形运算法

蝶形运算法是一种基于FFT&#xff08;Fast Fourier Transform&#xff09;算法的计算方法&#xff0c;其基本思想是将长度为N的DFT分解成若干个长度为N/2的DFT计算&#xff0c;并通过不断的合并操作得到最终的结果。该算法也称为“蝴蝶算法”&#xff0c;因为它的计算过程中需要…

[Android 四大组件] --- Activity

1 Activity是什么 ​​Activity​​是一个Android的应用组件&#xff0c;它提供屏幕进行交互。每个Activity都会获得一个用于绘制其用户界面的窗口&#xff0c;窗口可以充满哦屏幕也可以小于屏幕并浮动在其他窗口之上。 一个应用通常是由多个彼此松散联系的Activity组成&…

vue3学习笔记

语句直接写在<script setup></script>内 1.父组件向子组件传值 子组件&#xff08;名字cs.vue&#xff09;&#xff1a; <template><h1 >{{ msg }}</h1> </template><script setup> defineProps({msg: String }) </script>父…

LeetCode--HOT100题(47)

目录 题目描述&#xff1a;105. 从前序与中序遍历序列构造二叉树&#xff08;中等&#xff09;题目接口解题思路代码 PS: 题目描述&#xff1a;105. 从前序与中序遍历序列构造二叉树&#xff08;中等&#xff09; 给定两个整数数组 preorder 和 inorder &#xff0c;其中 preo…

【AI测试】python文字图像识别tesseract

[AI测试]python文字图像识别tesseract github官网&#xff1a;https://github.com/tesseract-ocr/tesseract python版本&#xff1a;https://github.com/madmaze/pytesseract OCR&#xff0c;即Optical Character Recognition&#xff0c;光学字符识别&#xff0c;是指通过扫…

工程制造领域:企业IT架构

一、IT组织规划架构图 1.1 IT服务保证梯队与指导思想 二、整体业务规划架构图 三、数据化项目规划架构图 四、应用系统集成架构图

CSS Flex布局

前言 Flex布局&#xff08;弹性盒子布局&#xff09; 是一种用于在容器中进行灵活和自适应布局的CSS布局模型。通过使用Flex布局&#xff0c;可以更方便地实现各种不同尺寸和比例的布局&#xff0c;使元素在容器内自动调整空间分配。 目录 容器属性 &#x1f341;display属性 &…

数据结构与算法复杂度介绍

目录 一、基本概念 二、时间复杂度 【2.1】时间复杂度概念 【2.2】大O的渐进表示法 【2.3】举例时间复杂度计算 三、空间复杂度 一、基本概念 数据结构&#xff1a;相互之间存在一种或者多种特定关系的数据元素的集合。在逻辑上可以分为线性结构&#xff0c;散列结构、树…

单片机电子元器件-按键

电子元器件 按键上有 四个引脚 1 2 、 3 4 按下之后 导通 1 3 、 2 4 初始导通 通常按键开关为机械弹性开关&#xff0c;开关在闭合不会马上稳定的接通&#xff0c;会有一连串的抖动 抖动时间的长短有机械特性来决定的&#xff0c;一般为5ms 到10 ms 。 消抖的分类 硬件消…

【大数据Hive】hive 加载数据常用方案使用详解

目录 一、前言 二、load 命令使用 2.1 load 概述 2.1.1 load 语法规则 2.1.2 load语法规则重要参数说明 2.2 load 数据加载操作演示 2.2.1 前置准备 2.2.2 加载本地数据 2.2.3 HDFS加载数据 2.2.4 从HDFS加载数据到分区表中并指定分区 2.3 hive3.0 load 命令新特性 …

vue项目配置MongoDB的增删改查操作

在Vue中配置MongoDB的增删改查操作&#xff0c;需要先安装mongoose模块来连接MongoDB数据库。 1. 在Vue项目的根目录中&#xff0c;使用命令行安装mongoose模块&#xff1a; npm install mongoose --save 2. 找到启动node的app.js文件&#xff08;我这里是在server文件中&…

Java开发之Redis(面试篇 持续更新)

文章目录 前言一、redis使用场景1. 知识分布2. 缓存穿透① 问题引入② 举例说明③ 解决方案④ 实战面试 3. 缓存击穿① 问题引入② 举例说明③ 解决方案④ 实战面试 4. 缓存雪崩① 问题引入② 举例说明③ 解决方案④ 实战面试 5. 缓存-双写一致性① 问题引入② 举例说明③ 解决…

Linux执行命令

命令格式 主命令 选项 参数&#xff08;操作对象&#xff09;例如&#xff1a; 修改主机名 hostname set-hostname 新名称显示/目录下的文件的详细信息 ls -l /命令 内置命令&#xff08;builtin&#xff09;&#xff1a;shell程序自带的命令。 外部命令&#xff1a;有独立…

多通道振弦数据记录仪应用桥梁安全监测的关键要点

多通道振弦数据记录仪应用桥梁安全监测的关键要点 随着近年来桥梁建设和维护的不断推进&#xff0c;桥梁安全监测越来越成为公共关注的焦点。多通道振弦数据记录仪因其高效、准确的数据采集和处理能力&#xff0c;已经成为桥梁安全监测中不可或缺的设备。本文将从以下几个方面…

【JavaEE】_HTML

目录 1.HTML结构 2. HTML常用标签 2.1 注释标签 2.2 标题标签&#xff1a;h1~h6 2.3 段落标签&#xff1a;p 2.4 换行标签&#xff1a;br 2.5 格式化标签 2.6 图片标签&#xff1a;img 2.7 超链接标签&#xff1a;a 2.8 表格标签 2.9 列表标签 2.10 表单标签 2.10…

PPO算法

PPO算法 全称Proximal Policy Optimization&#xff0c;是TRPO(Trust Region Policy Optimization)算法的继承与简化&#xff0c;大大降低了实现难度。原论文 算法大致流程 首先&#xff0c;使用已有的策略采样 N N N条轨迹&#xff0c;使用这些轨迹上的数据估计优势函数 A ^ …