【JAVA多线程】线程的状态控制

目录

1.JDK中的线程状态

2.基础操作

2.1关闭

2.2中断

2.3.等待、唤醒

2.4.阻塞、唤醒


1.JDK中的线程状态

在JDK的线程体系中线程一共6种状态:

  • NEW(新建): 当线程对象创建后,但尚未启动时,线程处于新建状态。
  • RUNNABLE(可运行): 当线程调用start()方法后,线程将变为可运行状态。此时线程可能正在运行,也可能正在等待CPU分配时间片。
  • BLOCKED(阻塞): 这个状态通常指线程在等待锁。当多个线程试图同时访问同步块或方法时,没有获得锁的线程将被阻塞。
  • WAITING(等待): 线程进入等待状态是因为调用了Object.wait(), Thread.join(), 或LockSupport.park()等方法。等待状态的线程不会竞争锁,也不会消耗CPU时间。
  • TIMED_WAITING(定时等待): 类似于等待状态,但线程将在一定时间后自动恢复到可运行状态,例如调用Thread.sleep(long millis)或wait(long timeout)。
  • TERMINATED(终止): 线程执行完毕或因异常而终止,将进入终止状态。

线程状态之间的转换如下:

  • NEW -> RUNNABLE: 调用线程的start()方法后发生。
  • RUNNABLE -> BLOCKED: 当线程尝试获取一个已被其他线程持有的锁时发生。
  • RUNNABLE -> WAITING/TIMED_WAITING: 当线程调用等待方法时发生。
  • BLOCKED -> RUNNABLE: 当线程获得了锁,或者锁被释放时发生。
  • WAITING/TIMED_WAITING -> RUNNABLE: 当等待条件满足,如其他线程调用了notify()或notifyAll(),或者定时等待时间到期时发生。
  • RUNNABLE -> TERMINATED: 当线程的run()方法完成或抛出未捕获的异常时发生。

其中需要值得重点关注的是等待(wait、timed_waiting)和阻塞(blocked):

  • 等待,线程不往下执行,不让出cpu资源

  • 阻塞,线程不往下执行,让出cpu资源。

其实在操作系统中线程一共就三大类状态,运行、阻塞、就绪,JDK中的等待和阻塞都归为阻塞这一大类状态中,等待又叫轻量级阻塞,阻塞又叫重量级阻塞。当在JDK的多线程体系里有大量的地方会用到等待/唤醒、阻塞/唤醒的两套api:

  • wait()阻塞线程、notify()唤醒线程

  • usfe.park()阻塞线程、usfe.unpark()唤醒线程

2.基础操作

2.1关闭

JAVA提供stop()、destory()两个函数来强制杀死线程,但是这两个方法已经被废弃,因为它们可能导致数据不一致和其他严重的副作用,比如资源泄漏。Thread.stop()方法会立即停止线程,并且会抛出ThreadDeath错误,这可能会导致线程在未清理资源的情况下突然终止。

推荐使用以下两种方式进行线程的关闭:

  • 守护线程,守护线程是一种特殊的线程类型,它不会阻止JVM的关闭。当JVM中不再有非守护线程运行时,JVM会自动退出,即使还有守护线程在运行。

  • 标志位,使用标志位是一种常见的线程终止策略,线程会在每次循环中检查标志位的状态,如果标志位被设置为true,则线程会退出循环并终止。

守护线程代码示例:

public class DaemonThreadExample {public static void main(String[] args) throws InterruptedException {Thread daemonThread = new Thread(() -> {while (true) {System.out.println("Daemon thread running...");try {Thread.sleep(1000);} catch (InterruptedException e) {Thread.currentThread().interrupt();System.out.println("Daemon thread interrupted.");break;}}});daemonThread.setDaemon(true); // 设置为守护线程daemonThread.start();// 主线程休眠一段时间后退出,此时守护线程也会随之退出Thread.sleep(5000);}
}

标志位代码示例:

public class FlagBasedTermination {private volatile boolean stopRequested = false;public void requestStop() {this.stopRequested = true;}public void runTask() {while (!stopRequested) {// 执行任务...System.out.println("Task running...");try {Thread.sleep(1000);} catch (InterruptedException e) {Thread.currentThread().interrupt();System.out.println("Task interrupted.");break;}}}public static void main(String[] args) throws InterruptedException {FlagBasedTermination task = new FlagBasedTermination();Thread taskThread = new Thread(task::runTask);taskThread.start();// 主线程休眠一段时间后请求停止子线程Thread.sleep(5000);task.requestStop();}
}

2.2中断

中断,不是指中断线程,而是指中断阻塞,唤醒线程。只有轻量级阻塞能够被中断,重量级阻塞不能被中断。

  • 轻量级阻塞,即等待状态,对应的JAVA线程中的状态有:

    • WAITING

    • TIMED_WAITING

  • 重量级阻塞,即阻塞状态,对应的JAVA线程中的状态有:

    • BLOCKED

JAVA提供了两个与中断阻塞相关的函数:

  • t.isInterrupted()

    非静态函数,读取中断标志位,不重置中断标志位。

  • Thread.interrupted()

    静态函数,给线程发送一个唤醒信号,如果是处于轻量级阻塞的线程收到唤醒信号后会被唤醒,重置中断标志位,并且抛出InterruptedException。

2.3.等待、唤醒

线程的等待状态是指线程由于一些原因先不向下执行,但暂时不放弃资源,只是稍作等待而已。等待和阻塞不同,阻塞是本来就不该执行,所以等待是可以发生在任何地方的,因此JDK给每个对象都设计了一个监视器锁(Monitor),这是Java虚拟机(JVM)底层为线程同步做出的支持。有了这个Monitor后JDK支持了线程在任何对象上先暂时等待,因为线程可以暂时躺在这个Monitor上。并给Object设计了如下api用来操作线程进行等待和唤醒:

  • wait(),等待。
  • notify(),唤醒。

在使用等待、唤醒时有两点注意事项:

  • 持有锁

    即wait()、notify()需要进行互斥,不可能一边notify(),一边wait()。

    代码示例:

  • 释放锁

    由于wait()是在同步代码块中执行的,wait()时要注意锁的释放,否则会死锁。

代码示例:

public class WaitNotifyDemo {private static final Object lock = new Object();private static boolean ready = false;public static void main(String[] args) {Thread producer = new Thread(() -> {synchronized (lock) {System.out.println("Producer: Waiting for consumer...");lock.notify();try {Thread.sleep(1000);} catch (InterruptedException e) {Thread.currentThread().interrupt();}ready = true;}});Thread consumer = new Thread(() -> {synchronized (lock) {try {System.out.println("Consumer: Waiting for notification...");lock.wait();} catch (InterruptedException e) {Thread.currentThread().interrupt();}if (ready) {System.out.println("Consumer: Received notification, ready is true.");}}});consumer.start();producer.start();}
}

2.4.阻塞、唤醒

阻塞唤醒有工具类:

LockSupport.park(), LockSupport.unpark(Thread thread)

其实底层调的都是线程原语:

JDK操作线程阻塞用的都是这个原语,底层就是用native调的操作系统的接口:

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

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

相关文章

python毕业设计选题校园食堂菜谱推荐系统

✌网站介绍:✌10年项目辅导经验、专注于计算机技术领域学生项目实战辅导。 ✌服务范围:Java(SpringBoo/SSM)、Python、PHP、Nodejs、爬虫、数据可视化、小程序、安卓app、大数据等设计与开发。 ✌服务内容:免费功能设计、免费提供开题答辩P…

stack(leetcode练习题,牛客)

文章目录 STL用法总结32 最长有效括号思路代码 496 下一个最大元素思路代码 856 括号的分数思路 最优屏障思路代码 STL用法总结 关于stack的知识,可以看点击查看上面的博客,以下题目前三个全在leetcode,最后一个在牛客 32 最长有效括号 思路…

【北京迅为】《i.MX8MM嵌入式Linux开发指南》-第三篇 嵌入式Linux驱动开发篇-第五十章 Linux设备树

i.MX8MM处理器采用了先进的14LPCFinFET工艺,提供更快的速度和更高的电源效率;四核Cortex-A53,单核Cortex-M4,多达五个内核 ,主频高达1.8GHz,2G DDR4内存、8G EMMC存储。千兆工业级以太网、MIPI-DSI、USB HOST、WIFI/BT…

代理IP在数据采集中具体有哪些作用

在当今信息化高速发展的时代,从市场趋势分析到消费者行为预测,从产品优化到服务改进,大数据都在其中扮演着不可或缺的角色。但数据的采集、整理和分析并非易事,特别是在面对海量的网络数据时,我们往往需要借助一些技术…

前端 SSE 长连接

使用 const options {withCredentials: true, // 默认 false}const eventSource new EventSource(/api, options);eventSource.addEventListener(open, () > {});eventSource.onmessage (event) > {}; // 或addEventListener(message,callback)eventSource.addEvent…

程序员拔火罐技能的分享

一.背景 之前为了考“中医康复理疗师”的证书,自学了拔火罐。自学主要是在自己大腿上练习,然后拿家人做小白鼠。后来考试没有那么严格也就顺利通过了。操作过程中,积累的一些小知识,分享给大家,有空在家里给家人服务体…

CTF ssrf 基础入门 (一)

0x01 引言 我发现我其实并不是很明白这个东西,有些微妙,而且记忆中也就记得Gopherus这个工具了,所以重新学习了一下,顺便记录一下吧 0x02 辨别 我们拿到一个题目,他的名字可能就是题目类型,但是也有可能…

FastAPI(七十三)实战开发《在线课程学习系统》接口开发-- 回复留言

源码见:"fastapi_study_road-learning_system_online_courses: fastapi框架实战之--在线课程学习系统" 之前文章分享FastAPI(七十二)实战开发《在线课程学习系统》接口开发-- 留言列表开发,这次我们分享如何回复留言 按…

Jenkins+Gitlab持续集成综合实战

一、持续集成应用背景: DevOps:(英文Development(开发)和Operations(技术运营)的组合)是一组过程、方法与系统的统称,用于促进开发(应用程序/软件工程&#…

NCRE3 2-1 网络总体设计基本方法

这部分记忆的比较多 概览 设计网络建设总体目标确定网络系统方案设计原则网络系统总体设计设计网络拓扑结构进行网络设备选型网络系统安全设计 设计网络建设总体目标 这部分视频没说到 确定网络系统方案设计原则 这部分视频没说到 网络系统总体设计 核心层网络结构设计 …

嵌入式Linux八股(四)——MCURTOS

嵌入式Linux八股完整文章目录 嵌入式Linux八股(一)——语言篇_嵌入式软件八股-CSDN博客 嵌入式Linux八股(二)——Linux_linux嵌入式的八股文有哪些-CSDN博客 嵌入式Linux八股(三)——计算机基础_嵌入式哪…

【Vue实战教程】之Vue工程化项目详解

Vue工程化项目 随着多年的发展,前端越来越模块化、组件化、工程化,这是前端发展的大趋势。webpack是目前用于构建前端工程化项目的主流工具之一,也正变得越来越重要。本章节我们来详细讲解一下如何使用webpack搭建Vue工程化项目。 1 使用we…

基于Python的哔哩哔哩国产动画排行数据分析系统

需要本项目的可以私信博主,提供完整的部署、讲解、文档、代码服务 随着经济社会的快速发展,中国影视产业迎来了蓬勃发展的契机,其中动漫产业发展尤为突出。中国拥有古老而又璀璨的文明,仅仅从中提取一部分就足以催生出大量精彩的…

源码拆解SpringBoot的自动配置机制

SpringBoot相比于Spring系列的前作,很大的一个亮点就是将配置进行了简化,引入了自动化配置,仅靠几个注解和yml文件就取代了之前XML的繁琐配置机制,这也是SpringBoot的独有特点,下面我们从源码角度,一点点拆…

Golang | Leetcode Golang题解之第273题整数转换英文表示

题目: 题解: var (singles []string{"", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine"}teens []string{&…

IEC61850 协议解读

1. IEC61850 协议介绍 IEC 61850 是定义 变电站 自动化系统 中 设备 及设备之间相互交互的 国际标准。 给出英文定义:IEC 61850 is the international standard for defining devices within substation automation systems and how they interact with one anoth…

Java基础-Java多线程机制

(创作不易,感谢有你,你的支持,就是我前行的最大动力,如果看完对你有帮助,请留下您的足迹) 目录 一、引言 二、多线程的基本概念 1. 线程与进程 2. 多线程与并发 3. 多线程的优势 三、Java多线程的实…

git命令使用详细介绍

1 环境配置 设置的信息会保存在~/.gitconfig文件中 查看配置信息 git config --list git config user.name设置用户信息 git config --global user.name "有勇气的牛排" git config --global user.email “1809296387qq.com”2 获取Git仓库 2.1 本地初始化一个仓…

Android Studio入门级教程(二)——项目开发基础(Java新手向))持续更新ing

目录 前言: 一.使用Log工具打印日志 常见语法: 如何使用? 二.工程目录结构 三.编译配置文件build.gradle 四.运行配置文件AndroidManifest.xml 五.界面显示与逻辑处理 六.创建新的app页面 1.包含的步骤 在layout目录下创建XML文件…

uniapp集成安卓原生录屏插件以及使用

概述 我们知道UniApp的出现简化了开发者的工作流程,并减少了代码的重复编写。开发者可以使用一套代码编译到iOS、Android、以及各种小程序的应用,节省了人力和时间成本,但是涉及到与系统交互的时候,比如录屏、录音、录像、文件操…