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

news2024/12/23 9:51:36

在我的前一篇博客中直接介绍了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() {
            @Override
            public 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() {
            @Override
            public 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.coloradmin.cn/o/973017.html

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

相关文章

软件产品选型测试POC测试怎么做?

软件poc测试 一、基本概述 软件选型测试是在软件采购的前提或采购过程中&#xff0c;通过对待选软件进行测试比对&#xff0c;筛选出适合的软件。通过对候选的软件进行量化或非量化的横向比对测试&#xff0c;为建设单位选择产品、供货方参加投标提供较直接的依据。 二、测试…

现在的校招面试,管你是不是应届生

作者&#xff1a;小傅哥 博客&#xff1a;https://bugstack.cn 沉淀、分享、成长&#xff0c;让自己和他人都能有所收获&#xff01;&#x1f604; 一、他&#xff0c;上来打我&#xff01; 【字节】除了MQ解耦发奖&#xff0c;是否还有比MQ更优的解决方案&#xff1f;【字节】…

算法通关村16关 | 堆与滑动窗口问题结合

1. 堆与滑动窗口问题结合 题目 LeetCode239 给你一个整数数组nums&#xff0c;有一个大小为k的滑动窗口从数组的最左侧移动到数组的最右侧&#xff0c;你可以看到在滑动窗口内的k个数字&#xff0c;滑动窗口每次只向右移动一位&#xff0c;返回滑动窗口中的最大值。 思路 对于…

css 文字单行多行超出长度后显示 ...

0.超出… 1、单行文本超出 <div class"content">测试数据&#xff1a;css单行文本超出显示省略号--------</div><style> .content{width: 200px;height: 200px;overflow:hidden;white-space: nowrap;text-overflow: ellipsis;-o-text-overflow:el…

linux信号量

通过学习linux的信号量&#xff0c;对linux的信号量进行了编程。

QT(9.4)tcp通信,数据库,opencv,

作业&#xff1a; 1.服务器 头文件&#xff1a; #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QTcpSocket>//客户端头文件 #include <QMessageBox>//消息对话框头文件 #include <QTcpServer>//服务器头文件 #include <list>…

docker报错解决方法

ERROR: readlink /var/lib/docker/overlay2/l: invalid argument 注意&#xff1a;会清空已有安装 sudo service docker stop sudo rm -rf /var/lib/docker sudo service docker start

LeetCode 热题 100——无重复字符的最长子串(滑动窗口)

题目链接 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 题目解析 从s字符串中&#xff0c;去找出连续的子串&#xff0c;使该子串中没有重复字符&#xff0c;返回它的最长长度。 暴力枚举 依次以第一个、第二个、第三个等等为起点去遍历字符串&a…

Python小知识 - Python爬虫进阶:如何克服反爬虫技术

Python爬虫进阶&#xff1a;如何克服反爬虫技术 爬虫是一种按照一定的规则&#xff0c;自动抓取网页信息的程序。爬虫也叫网页蜘蛛、蚂蚁、小水滴&#xff0c;是一种基于特定算法的自动化程序&#xff0c;能够按照一定的规则自动的抓取网页中的信息。爬虫程序的主要作用就是从一…

springboot自动装配原理,手写一个starter。

文章目录 springboot自动装配原理手写starter手写starter总结&#xff1a; springboot自动装配原理 口述&#xff1a; springboot自动装配的话它其实就是只需要我们添加一个starter起步依赖&#xff0c;它就能完成这个依赖组件相关Bean的自动注入&#xff0c;其实就是自动的将…

ORB-SLAM2算法13之跟踪线程Tracking

文章目录 0 引言1 跟踪线程Tracking1.1 概述1.2 初始化1.2.1 单目初始化1.2.2 双目/RGBD初始化 1.3 跟踪方法1.3.1 恒速模型跟踪1.3.2 参考关键帧跟踪1.3.3 重定位跟踪 1.4 局部地图跟踪1.4.1 流程1.4.2 更新局部关键帧1.4.3 更新局部地图点1.4.4 进一步优化 1.5 关键帧生成1.5…

windows编程之线程同步万字总结(创建线程,互斥对象,互斥事件,信号量,关键段,多线程群聊服务器)

文章目录 创建线程方法一_beginthreadex函数讲解使用示例&#xff1a; 方法二CreateThread函数讲解:使用示例: 互斥对象:创建互斥对象CreateMutex 互斥事件介绍创建或打开一个未命名的互斥事件对象 信号量介绍信号量的相关函数使用示例 关键段相关函数错误使用示例正确使用示例…

vite介绍

vite vite是一种新的前端构建工具&#xff0c;vite借助了浏览器对ESM的支持&#xff0c;采用和传统webpack打包完全不一致的unbundle打包机制&#xff1b; vite的快主要体现在两个方面&#xff0c;快速的冷启动和快速的热更新 快速的冷启动&#xff1a;vite只需启动一台静态页…

如何将Express项目部署到Vercel

什么是Vercel&#xff1f; 想必好多前端同学都知道Vercel吧&#xff01;如果还不了解的同学也没关系&#xff0c;好好看这篇文章&#xff0c;认识认识Vercel&#xff0c;我想对你部署项目有一定帮助。 Vercel 是一个云平台&#xff0c;用于托管和部署静态网站、前端应用程序以…

2023年无形资产评估研究报告

第一章 无形资产概况 1.1 定义 无形资产是一种缺乏物质实体的资产。例如&#xff0c;专利、版权、特许权、商誉、商标和商号&#xff0c;以及软件等。这与物质资产&#xff08;如机器、建筑等&#xff09;和金融资产&#xff08;如政府证券等&#xff09;形成了对比。无形资产…

Mybatis 动态SQL – 使用choose标签动态生成条件语句

之前我们介绍了if,where标签的使用&#xff1b;本篇我们需要在if,where标签的基础上介绍如何使用Mybatis提供的choose标签动态生成条件语句。 如果您对if,where标签动态生成条件语句不太了解&#xff0c;建议您先进行了解后再阅读本篇&#xff0c;可以参考&#xff1a; Mybat…

将序数与比特币智能合约集成:第 1 部分

将序数与比特币智能合约集成&#xff1a;第 1 部分 最近&#xff0c;比特币序数在区块链领域引起了广泛关注。 据称&#xff0c;与以太坊 ERC-721 等其他代币标准相比&#xff0c;Ordinals 的一个主要缺点是缺乏对智能合约的支持。 我们展示了如何向 Ordinals 添加智能合约功…

插入排序,选择排序,交换排序,归并排序和非比较排序(C语言版)

前言 所谓排序&#xff0c;就是将一组数据按照递增或者递减的方式进行排列&#xff0c;让这组数据变得有序起来。排序在生活中运用的是十分广泛的&#xff0c;各行各业都用到了排序&#xff0c;比如我们在网购的时候就是按照某种排序的方式来选择东西的。所以去了解排序的实现也…

vue 使用qrcode生成二维码并可下载保存

安装qrcode npm install qrcode --save代码 <template><div style"display: flex; flex-direction: column; align-items: center; justify-content center;"><div>查看溯源码&#xff0c;<a id"saveLink" style"text-decorati…

Ae 效果:CC Glue Gun

生成/CC Glue Gun Generate/CC Glue Gun CC Glue Gun&#xff08;CC 胶水枪&#xff09;可以用于生成仿佛由胶水枪绘制的线条或图案&#xff0c;它模拟了胶水枪绘制在不同表面上的纹理和反光效果。 CC Glue Gun 效果实质上是通过设置画笔笔触的位置来构成画笔描边路径&#xff…