【java 多线程】并发设计模式-两阶段终止模式(对interrupt的理解)

news2025/4/5 14:53:18

📋 个人简介

  • 💖 作者简介:大家好,我是阿牛,全栈领域优质创作者。😜
  • 📝 个人主页:馆主阿牛🔥
  • 🎉 支持我:点赞👍+收藏⭐️+留言📝
  • 📣 系列专栏:并发编程🍁
  • 💬格言:要成为光,因为有怕黑的人!🔥
    请添加图片描述

目录

    • 📋 个人简介
  • 前言
    • interrupt 的理解
    • isInterrupted() 和 interrupted的区别
    • 并发设计模式-两阶段终止模式
  • 结语

前言

今天总结一个设计模式中的属于并发里的一个设计模式,叫两阶段终止模式,看这个名字可能大家都不知道是怎样的,实际上这个就是在考验你对多线程中的interrupt()方法的理解!从而实现线程优雅的退出!本文全是细节,初学者请仔细看,多理解!

interrupt 的理解

来看看jdk手册对于interrupt的介绍:
在这里插入图片描述
还是比较抽象!我结合自己的理解说一说:

Interrupt 这个词很容易让人产生误解。从字面意思来看,好像是说一个线程运行到一半,把它中断了,然后抛出了 InterruptedException 异常,其实并不是!请看下面这段代码:

public class Demo {
    public static void main(String[] args) throws InterruptedException {
        // 创建一个线程 ---我用简洁写法,Thread里面是Runnable接口(是一个函数式接口)的lambda表达式
        Thread t1 = new Thread(()->{
            while(true){
                System.out.println(Thread.currentThread().getName() + "线程运行中...");
            }
        },"t1");
        t1.start();

        // 主线程
        Thread.sleep(1000);  // 让主线程停一会,使得t1线程执行一会
        t1.interrupt();
        System.out.println("interrupt执行啦!");
    }
}

在这里插入图片描述
上面这个代码主线程调用interrupt方法并不会使得线程停止,也没有抛出异常!那么调用这个方法后就什么都没有干嘛,其实不是,它会将打断标记置为true,凭借此打断标记可通过代码判断从而使得线程停止!

public class Demo {
    public static void main(String[] args) throws InterruptedException {
        // 创建一个线程 ---我用简洁写法,Thread里面是Runnable接口(是一个函数式接口)的lambda表达式
        Thread t1 = new Thread(()->{
            while(true){
                System.out.println(Thread.currentThread().getName() + "线程运行中...,打断标记为:"+ Thread.currentThread().isInterrupted());
            }
        },"t1");
        t1.start();

        // 主线程
        Thread.sleep(500);  // 让主线程停一会,使得t1线程执行一会
        t1.interrupt();
        System.out.println("interrupt执行啦!");
    }
}

使用isInterrupted()就可以判断输出线程的打断状态,可以在控制台很清晰的看到,当t1.interrupt()执行后,打断状态从false变为true!

那什么情况下才会抛出InterruptedException异常呢!正如jdk文档中说的,只有sleep,wait,join这三个轻量级阻塞(可以被中断的阻塞,即唤醒线程)声明了InterruptedException异常,可以抛出InterruptedException异常

这也间接说明了interrupt的另一个作用,唤醒阻塞的线程,使得它不再阻塞,转而抛出InterruptedException异常,使得线程顺利执行或者顺利执行完毕,让出cpu的时间片!

我们对上面案例代码进行改变:

public class Demo {
    public static void main(String[] args) throws InterruptedException {
        // 创建一个线程 ---我用简洁写法,Thread里面是Runnable接口(是一个函数式接口)的lambda表达式
        Thread t1 = new Thread(()->{
            while(true){
                try {
                    Thread.sleep(1000);
                    System.out.println(Thread.currentThread().getName() + "线程运行中...,打断标记为:"+ Thread.currentThread().isInterrupted());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    // 输出在sleep状态被打断后的打断标记
                    System.out.println(Thread.currentThread().isInterrupted());
                }
            }
        },"t1");
        t1.start();

        // 主线程
        Thread.sleep(2000);  // 让主线程停一会,使得t1线程执行一会
        t1.interrupt();
        System.out.println("interrupt执行啦!");
    }
}

在这里插入图片描述
可以看到抛出的异常显示t1线程是在睡眠过程中被打断的,抛出了异常,即唤醒了阻塞线程,使得其继续执行,特别需要注意的是,抛出异常后他的打断标记会重置为false,并不是想像中的true!

isInterrupted() 和 interrupted的区别

interrupted方法是Thread的静态方法,这里就不看jdk文档了,可能读不明白,还会产生误导!
这两个函数都是线程用来判断自己是否收到过中断信号的,前者是非静态函数,后者是静态函数。二者的区别在于,前者只是读取中断状态,不修改状态;后者不仅读取中断状态,还会重置中断标志位!

看下面这个例子:

public class Demo {
    public static void main(String[] args) throws InterruptedException {
        // 创建一个线程 ---我用简洁写法,Thread里面是Runnable接口(是一个函数式接口)的lambda表达式
        Thread t1 = new Thread(()->{
            while(true){
                System.out.println(Thread.currentThread().getName() + "线程运行中...,打断标记为:"+ Thread.interrupted());
            }
        },"t1");
        t1.start();

        // 主线程
        Thread.sleep(500);  // 让主线程停一会,使得t1线程执行一会
        t1.interrupt();
        System.out.println("interrupt执行啦!");
    }
}

在这里插入图片描述

当执行t1.interrupt();后,t1线程打断状态本应该转变为true,但由于调用了Thread.interrupted(),重置了打断状态,因此打断状态一直输出false!

并发设计模式-两阶段终止模式

先来看看两阶段准直模式是啥:

两阶段终止模式是一种并发设计模式,它用于优雅地终止线程。它将终止过程分成两个阶段,第一阶段由线程T1向线程T2发送终止指令,第二阶段是由线程T2响应终止指令。这种模式通过将停止线程这个动作分解为准备阶段和执行阶段这两个阶段,提供了一种通用的用于优雅地停止线程的方法!

所以说,两阶段终止模式的目的是优雅地终止线程,很多人就会产生疑问,不就是停止线程吗,我stop不行吗,为啥要用这模式优雅的停止?

这里主要还是线程安全的问题,相信都看过stop等方法不建议用吧,强行停止线程可能会生成死锁等线程安全问题(停止了,锁未释放,其他线程拿不到锁),优雅地终止线程可以确保线程在终止之前完成它应该完成的任务并执行一些收尾工作,从而保证数据和业务的安全性。如果随意终止线程,可能会导致数据丢失或损坏,或者导致程序运行不稳定。

说了这么多,也看了上面对interupt的介绍,下面就来看看两阶段终止模式的代码实现吧!

package 两阶段终止模式;

/**
 * @Author:Aniu
 * @Date:2023/4/10 21:38
 * @description 并发设计模式-两阶段终止模式-interrupt
 */
public class Demo {
    public static void main(String[] args) throws InterruptedException {
        TwoStageTermination t1 = new TwoStageTermination();
        t1.start();
        Thread.sleep(3000);
        t1.stop();
    }
}

class TwoStageTermination{
    private Thread monitor;

    // 启动线程
    public void start(){
        monitor = new Thread(() -> {
            while(true){
                Thread currentThread = Thread.currentThread();
                // 根据打断标记,退出循环,线程结束
                if(currentThread.isInterrupted()){   // isInterrupted() 打断标记的状态
                    System.out.println("打断标记:true, 线程退出!");
                    break;
                }
                try {
                    Thread.sleep(1000);  // 情况一:睡眠中打断,抛出InterruptedException异常,唤醒线程,清除打断标记:false,需要手动重置打断标记为true
                    System.out.println("线程运行中···"); // 情况二:线程正常运行,打断后,线程不会自动停止,打断标记置为:true,用打断标记写if判断
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    currentThread.interrupt(); // 再次打断:重置打断标记为true,使得循环退出
                }
            }
        });

        monitor.start();
    }

    // 打断线程
    public void stop(){
        monitor.interrupt();   // interrupt() 打断线程
    }
}

在这里插入图片描述
上面有两种情况,一种是在睡眠中打断退出,一种是在运行中打断退出,结合isInterrupted方法实现了线程的优雅退出!

整体流程图如下:
在这里插入图片描述

结语

如果你觉得博主写的还不错的话,可以关注一下当前专栏,博主会更完这个系列的哦!也欢迎订阅博主的其他好的专栏。

🏰系列专栏
👉flask框架入门到实战
👉软磨 css
👉硬泡 javascript

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

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

相关文章

惊艳!阿里出品“Java全栈进阶宝典”,广度与深度齐头并进

前言 据有关数据统计,目前来看,大大小小的招聘网站上面,Java岗的招聘量仍然是最多的,基本是其他语言的3倍以上,由于目前Java所处的统治级地位,单就数量来看,Java语言实现的系统是海量的&#x…

linux安装MongoDB

环境说明 系统CentOS:CentOS7 mongodb版本 4.2.24 下载 官网下载地址 Download MongoDB Community Server | MongoDB 某云盘 链接:https://pan.baidu.com/s/1G4AC3h5rvz9WM3fx4gJzbA 提取码:ojkl 上传解压 在根目录下创建opt文件夹…

【数据分析之道-基础知识(九)】推导式

文章目录专栏导读1、前言2、列表推导式3、集合推导式4、字典推导式5、元组推导式专栏导读 ✍ 作者简介:i阿极,CSDN Python领域新星创作者,专注于分享python领域知识。 ✍ 本文录入于《数据分析之道》,本专栏针对大学生、初级数据分…

C语言学习1--------Visual Studio集成开发环境的搭建

C语言学习1--------Visual Studio集成开发环境的搭建适合初学者适用集成开发环境下载 Visual Studio 2019安装 Visual Studio 2019安装工作负载为C自定义安装位置激活 Visual Studio适合初学者适用集成开发环境 建议初学者适用最新的——Visual Studio 2019为集成开发环境。 部…

【数据库原理 • 五】数据库安全性与完整性

前言 数据库技术是计算机科学技术中发展最快,应用最广的技术之一,它是专门研究如何科学的组织和存储数据,如何高效地获取和处理数据的技术。它已成为各行各业存储数据、管理信息、共享资源和决策支持的最先进,最常用的技术。 当前…

【C++11那些事儿(一)】

文章目录一、C11简介二、列表初始化2.1 C98中{}的初始化问题2.2 C11中的列表初始化三、各种小语法3.1 auto3.2 decltype3.3 nullptr3.4 范围for四、STL中的一些变化五、左/右值引用和移动语义(本篇重点)5.1 做值引用和右值引用5.2 左值引用与右值引用比较…

【快乐手撕LeetCode题解系列】—— 复制带随机指针的链表

【快乐手撕LeetCode题解系列】—— 复制带随机指针的链表😎前言🙌复制带随机指针的链表🙌画图分析:😍思路分析:😍源代码分享:😍总结撒花💞😎博客昵…

shell结构化命令中for命令

shell脚本编程系列 for var in list docommands done读取列表中的值 每次遍历值列表时,for命令会将列表中的下一个值赋值给变量 #!/bin/bash# basic for commandfor test in Alabama Alaska Arizona Arkansas California Coloradodoecho The next state is $testdo…

第07章_面向对象编程(进阶)

第07章_面向对象编程(进阶) 讲师:尚硅谷-宋红康(江湖人称:康师傅) 官网:http://www.atguigu.com 本章专题与脉络 1. 关键字:this 1.1 this是什么? 在Java中,this关键字不算难理解…

关于图像分割的预处理 transform

目录 1. 介绍 2. 关于分割中的 resize 问题 3. 分割的 transform 3.1 随机缩放 RandomResize 3.2 随机水平翻转 RandomHorizontalFlip 3.3 随机竖直翻转 RandomVerticalFlip 3.4 中心裁剪 RandomCrop 3.5 ToTensor 3.6 normalization 3.7 Compose 4. 预处理结果可视…

WPF mvvm框架Stylet使用教程-基础用法

Stylet框架基础用法 安装Nuget包 在“管理Nuget程序包”中搜索Stylet,查看Stylet包支持的net版本,然后选择第二个Stylet.Start包进行安装,该包会自动安装stylet并且生成基本的配置 注意事项:安装时要把需要安装的程序设为启动项…

第06章_面向对象编程(基础)

第06章_面向对象编程(基础) 讲师:尚硅谷-宋红康(江湖人称:康师傅) 官网:http://www.atguigu.com 本章专题与脉络 学习面向对象内容的三条主线 Java类及类的成员:(重点&…

《QT+CGAL网格处理——网格重建》

QT+CGAL网格处理——网格重建 一、重建效果二、代码分析显示代码格式转换彩色网格显示三、后续一、重建效果 二、代码分析 显示 1、依旧采取VTK显示,参照《QT+PCL》; 2、点数据、网格数据依旧采用pcl数据结构,cgal处理完成后转换格式即可 界面参照:

微服务学习-SpringCloud -Nacos (心跳机制及健康检查源码学习)

文章目录心跳机制与健康检查流程图心跳机制与健康检查总结详细源码说明当多个服务进行注册时,如何解决注册表并发冲突问题?心跳机制与健康检查流程图 心跳机制与健康检查总结 微服务在启动注册Nacos时,会创建一个定时任务,定时向服务端发生…

基于文心一言的底层视觉理解,百度网盘把「猫」换成了「黄色的猫」

随着移动互联网的一路狂飙,手机已经成为人们的新器官。出门不带钥匙可以,不带手机却是万万不可以的。而手机上,小小的摄像头也越来越成为各位「vlogger」的口袋魔方。每天有超过数亿的照片和视频被上传到百度网盘中,这些照片和视频…

Nginx 实战-负载均衡

一、负载均衡今天学习一下Nginx的负载均衡。由于传统软件建构的局限性,加上一台服务器处理能里的有限性,在如今高并发、业务复杂的场景下很难达到咱们的要求。但是若将很多台这样的服务器通过某种方式组成一个整体,并且将所有的请求平均的分配…

Kafka的命令行操作

一、topic命令 下面Windows命令需要把cmd路径切换到bin/windows下。 而Linux命令只需要在控制台切换到bin目录下即可。 下面都以Windows下的操作为例,在Linux下也是一样的。 1.1 查看主题命令的参数 kafka-topics.bat # Windows kafka-topics.sh # Linux输…

机器学习中的数学——学习曲线如何区别欠拟合与过拟合

通过这篇博客,你将清晰的明白什么是如何区别欠拟合与过拟合。这个专栏名为白话机器学习中数学学习笔记,主要是用来分享一下我在 机器学习中的学习笔记及一些感悟,也希望对你的学习有帮助哦!感兴趣的小伙伴欢迎私信或者评论区留言&…

关于人工智能前沿信息获取的精品课程

通过观看在线课程可以很方便掌握获取人工智能前沿信息的方法,本文将介绍一些相关课程。 1. 武汉大学黄如花教授的信息检索中文课程知识面广泛,内容详尽,讲解清晰,课程的视频见课程 56。 ​ 课程 56 武汉大学黄如花教授的《信息检…

图神经网络GNN介绍

目录标题图神经网络基础图基本模块定义图的邻接矩阵点特征的更新(重构)多层GNN图卷积GCN模型GCN基本思想网络层数:基本计算图注意力机制graph attention networkT-GCN序列图神经网络图相似度图神经网络基础 图基本模块定义 三个特征&#x…