[Java EE] 多线程(二): 线程的创建与常用方法(下)

news2025/1/18 8:54:14

2.3 启动一个线程–>start()

之前我们已经看到了如何通过重写run()方法来创建一个线程对象,但是线程对象被创建出来并不意味着线程就开始运行了.

  • 覆写run方法是给线程提供了所要做的事情的指令清单
  • 创建线程对象就是把干活的人叫了过来.
  • 而调用start方法,就是喊一声"行动!!",线程会拿着事先准备好的指令清单去完成所要执行的事情.
  • 调用start方法,才是真正在操作系统中创建了PCB.
    下面我们用大家所感兴趣的领取任务类游戏来说明:
    原神,启动!!!
    在这里插入图片描述
  1. 首先,比如说我们要去任务列表那里领取任务,任务列表在你领取任务之前,就已经把你任务要做的事情规划好了,也就是该线程要做的任务,都规划在了run()方法中,这里的每一个任务就对应着一个线程,每一个任务中都有我们所要做的事情,也就是run()方法中所写的东西.
    在这里插入图片描述
  2. 比如说我们要做一个每日委托的任务,在你靠近任务区域之前会触发委托,我们已经准备就绪做任务了.此时便相当于创建了线程对象.
    在这里插入图片描述
  3. 我们在完成每日委托任务的时候,总是要进入战斗状态,打败怪物,来完成委托,当我们在怪物的身上打出了第一点伤害,我们就开始正式执行每日委托任务了,也就是线程通过start()方法正式启动.
    在这里插入图片描述

2.4 中断一个线程

⽬前常⻅的有以下两种⽅式:

  1. 通过共享的标记来进⾏沟通
  2. 调⽤interrupt()⽅法来通知

实例一:通过使用自定义变量来作为标识位

public class Demo1 {
    public static boolean isRunning = true;//把此变量先设置为true
    public static void main(String[] args) {
        Thread thread = new Thread(()->{
            while (isRunning){//isRunning为真,执行,为假,不执行
                System.out.println("hello tread");
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        thread.start();
        try {
            Thread.sleep(1500);//执行1.5s后修改标识位
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        isRunning = false;
    }
}

中断线程常用的方法如下:

方法说明
public void interrupt()中断对象关联的线程,如果线程正在阻塞,那么强制唤醒线程
public static boolean interrupted()判断当前线程是否中断
public boolean isinterrupted()判断对象关联的线程是否中断

补充:用Tread.currentTread()来获取当前线程的引用对象.所以判断当前线程是否中断就有了两个方法,第一个方法就是Tread.interrupted(),另一种就是Tread.currentTread().isinterrupted().

实例二:通过使用interrupt()方法来中断线程

public class Demo2 {
    public static void main(String[] args) {
        Thread thread = new Thread(()->{
            while (!Thread.currentThread().isInterrupted()){
                System.out.println("hello tread");
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        thread.start();
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        thread.interrupt();//中断线程
    }
}

这里使用interrupt()方法来中断一个线程与使用标志位的最大区别就是是否在线程休眠的时候强制唤醒线程.如果使用标志位的方法来终止线程,那么线程休眠的时候,就不会唤醒线程,线程依然处于休眠的状态,而使用interrupt()方法可以唤醒线程,使得线程触发sleep唤醒异常,最后在catch方法中来处理唤醒异常,也就是唤醒之后干什么,是可以由程序员自己决定的.

显然,我们在实际开发中,最多使用的是第二种方法,第一种方法不可唤醒线程,线程会继续阻塞,会使得线程的效率大大降低.

下面我们来展示几种线程被唤醒常见的异常处理方法:

  1. 直接终止线程
public class Demo2 {
    public static void main(String[] args) {
        Thread thread = new Thread(()->{
            while (!Thread.currentThread().isInterrupted()){
                System.out.println("hello tread");
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    break;//直接使用break终止线程
                }
            }
        });
        thread.start();
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        thread.interrupt();
    }
}

  1. 抛出异常,并终止线程
public class Demo2 {
    public static void main(String[] args) {
        Thread thread = new Thread(()->{
            while (!Thread.currentThread().isInterrupted()){
                System.out.println("hello tread");
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);//抛出异常
                }
            }
        });
        thread.start();
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        thread.interrupt();
    }
}

  1. 打印栈错误信息
public class Demo2 {
    public static void main(String[] args) {
        Thread thread = new Thread(()->{
            while (Thread.currentThread().isInterrupted()){
                System.out.println("hello tread");
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        thread.start();
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        thread.interrupt();
    }
}

但是这种方法我们会发现,打印栈错误信息后并不可以真正终止线程:
在这里插入图片描述
这是为什么呢?是因为我们在sleep被唤醒之后,会清除刚才的标志位,所以才会继续执行,所以线程是否终止,是由线程自己说了算,interrupt方法只是给出建议.

我们下面举例来区分它们几个:
下面有请我们的助教:灰太狼,红太狼登场.

  1. 红太狼让灰太狼出门抓羊,
  2. 灰太狼现在正在打游戏,灰太狼现在有两种选择,第一就是立刻停下手中的游戏去抓羊,就是使得线程立刻终止,直接break掉或者抛出异常
  3. 第二种选择就是先答应红太狼一声,等打完这把游戏之后,再去抓羊.就是打印栈错误信息之后并没有直接终止.
  4. 最后一中当然就是直接无视红太狼,继续打游戏,就是catch中什么都不写,把标志位改回来之后继续执行,下场就是…
    在这里插入图片描述
    平底锅伺候!!!

2.5 等待线程–>join()

有时,我们需要等待⼀个线程完成它的⼯作后,才能进行自己的下⼀步⼯作.所以我们便引入了join方法:重点一句话,谁调用join方法,谁就要进入阻塞状态去等待那个被调用join方法的线程结束

方法说明
public void join()等待线程结束(死等)
public void join(long millis)等待现场结束,最多等x毫秒
public void join(long millis,int nanos)比上一个方法更精确,可以精确到纳秒

我们下面拿一个具体的例子来说明:

public class Demo4 {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                System.out.println("tread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        thread.start();
        try {
            thread.join();//在这里,main线程会进入阻塞状态,等待tread线程结束
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("main tread is terminate");
    }
}

运行结果:
在这里插入图片描述
在main线程中调用join方法,有以下两种可能:

  1. 若t线程已经结束,此时join就会立即返回.
  2. 若t线程此时还没有结束,join就会继续阻塞等待,一直到t线程结束.

下面我们举一个例子来说明上述问题:
有请助教:达达利亚,钟离登场
钟离邀请达达利亚去万名堂去吃饭,钟离此时是main线程,是他主动发起的邀请,情况一就是钟离先到的早,此时钟离并没有打电话叫达达利亚,就一直等(main,join之后,进入阻塞状态,死等达达利亚)达达利亚到了之后,再去一起吃饭.
情况二就是达达利亚到的早,之后达达利亚直接打电话叫了钟离,钟离直接停下手中的活(join直接返回)下楼去和达达利亚一起去吃饭.
情况三,钟离也是一个有底线的人,由于达达利亚迟迟不肯到来,所以钟离不可以等了(join(限制时间)),再等肚子都要饿扁了,直接离开了在这里插入图片描述

[注意]
在一般情况下,我们不使用死等的的方式去等待线程响应,我们一般是有时间的等.

2.6 休眠当前线程

也是我们比较熟悉⼀组方法,有⼀点要记得,因为线程的调度是不可控的,所以,这个方法只能保证实际休眠时间是⼤于等于参数设定休眠时间.因为线程从回复就绪状态到CPU上执行还需要一定的时间.

public class Demo10 {
    public static void main(String[] args) throws InterruptedException {
        System.out.println(System.currentTimeMillis());
        Thread.sleep(3 * 1000);
        System.out.println(System.currentTimeMillis());
    }
}

在这里插入图片描述
我们看到执行的结果相减并不是准确的3000ms,有一定的时间差异.

3. 线程的状态

3.1 线程的状态分类

线程的状态,可以通过==getState()==方法来获取到.
• NEW:Tread对象已经创建,但是还没有start
• RUNNABLE:处于就绪状态
• BLOCKED:后续介绍
• WAITING:死等进入阻塞
• TIMED_WAITING:带有超时时间的等进入阻塞
• TERMINATED:线程已经终止,但是Tread对象还在,未被gc回收.

public class Demo8 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(()->{
            for (int i = 0; i < 5; i++) {
                System.out.println("tread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        Thread thread1 = new Thread(()->{
            for (int i = 0; i < 5; i++) {
                System.out.println("tread1");
                if (i == 1){
                    try {
                        thread.join(3000);//等待时间3s
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        System.out.println(thread.getState());
        thread1.start();
        thread.start();
        System.out.println(thread.getState());
        Thread.sleep(2000);
        System.out.println(thread1.getState());
        thread.join();
        thread1.join();
        System.out.println(thread.getState());
        System.out.println(thread.getState());
    }
}

运行结果:
在这里插入图片描述
把注释处的时间去掉:
thread.join();
可见tread1的状态在等待tread的过程中变为了WAITING.
在这里插入图片描述

3.2 线程状态的转移

一条主线三条支线:
在这里插入图片描述

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

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

相关文章

Java处理CSV类库:OpenCSV

一&#xff1a;CSV简介 Comma-Separated Values(CSV), 因分隔符没有严格指定规范标准&#xff0c;可以使用逗号&#xff0c;也可以使用其他字符&#xff08;如制表符\t、分号;等&#xff09;&#xff0c;所以CSV也称为 逗号分隔值或者字符分隔值。csv文件是使用纯文本来存储表…

【数据库】为什么要添加一个与业务无关的主键?

关注获取更多&#xff1a; 你是否想过&#xff0c;为什么mysql要设置一个自增的主键&#xff0c;或者使用uuid生成一个和业务无关的主键id&#xff0c;在数据库设计中&#xff0c;主键是用来唯一标识每一行数据的关键。通常情况下&#xff0c;我们会选择与业务相关的字段作为…

CMEF | 澳鹏Appen精彩亮相第89届中国国际医疗器械博览会

4月14日&#xff0c;为期四天的第89届中国国际医疗器械博览会&#xff08;CMEF&#xff09;盛大收官。如今&#xff0c;人们的健康需求在人口老龄化等一系列因素的影响下持续增长&#xff0c;这意味着卫生系统也面对着更多具有复杂健康需求的患者。信息化、数字化、智能化已经成…

Zynq学习笔记--数字视频帧以及同步信号

目录 1. 介绍 2. 重要概念 3. 仿真测试 4. 总结 1. 介绍 Zynq芯片&#xff0c;作为一款集成了高性能FPGA和ARM处理器的系统级芯片(SoC)&#xff0c;为视频处理提供了强大的硬件支持。该芯片内置的丰富视频方面的IP模块&#xff0c;使得从事视频处理项目的开发者能够高效、…

PHP 使用 PHPMailer 发送电子邮件

1. PHPMailer 介绍 phpMailer 是一个非常强大的 php 发送邮件扩展包&#xff0c;可以设定发送邮件地址、回复地址、邮件主题、html邮件内容和上传附件等&#xff0c;使用起来非常方便。它目前有着有近 4 千万的下载量&#xff0c;是 PHP 开发者实现邮件发送功能的首选扩展包 它…

阅读小程序|基于微信阅读网站小程序的系统设计与实现(源码+数据库+文档)

目录 基于微信阅读网站小程序 一、前言 二、系统设计 三、系统功能设计 小程序端&#xff1a; 后台 管理员进入指定功能操作区之后可以管理图书订单。其页面见下图。管理员审核订单&#xff0c;查看订单是否支付&#xff0c;为购买图书的用户推送对应的章节。 管理员进…

西瓜书学习——第一、二章笔记

[] 什么是机器学习? 研究关于“学习算法”(一类能从数据中学习出其背后潜在规律的算法)的一门学科。 PS:深度学习指的是神经网络那一类学习算法&#xff0c;因此是机器学习的子集。 假设空间和版本空间 举个栗子:假设现已收集到某地区近几年的房价和学校数量数据&#xf…

【基础物理实验】【AFM虚拟实验】基于AFM的物质表面微观结构及力学性质表征仿真实验(下)【北京航空航天大学】

本次实验&#xff08;上&#xff09;见博客&#xff1a;【基础物理实验】【AFM虚拟实验】基于AFM的物质表面微观结构及力学性质表征仿真实验&#xff08;上&#xff09;【北京航空航天大学】 本次实验&#xff08;中&#xff09;见博客&#xff1a;【基础物理实验】【AFM虚拟实…

『哈哥赠书 - 51期』-『数字风控体系:设计与实践』

⭐️ 赠书 - 《数字风控体系&#xff1a;设计与实践》 ⭐️ 数字风控概述 从2007年开始到2014年左右&#xff0c;高速移动网络和智能手机迅速在大众中普及&#xff0c;开启了移动互联网时代。在这个背景下&#xff0c;企业的产品与服务也出现了两个重要的趋势。 第一个趋势是…

(十三)C++自制植物大战僵尸游戏多用户存档实现(二)

植物大战僵尸游戏开发教程专栏地址http://t.csdnimg.cn/8UFMs UserData.h 在头文件中定义了枚举类型openUserDataReturnType&#xff0c;用于表示打开用户数据文件的返回状态。FileExistError表示文件存在但是打开错误&#xff0c;FileExistCorrect表示文件在且正确&#xff0…

活动理论的散点图

import pandas as pd import matplotlib.pyplot as plt# 假设您已经有一个名为 data.xlsx 的 Excel 文件 # 您可以使用以下代码读取数据# 读取 Excel 文件 try:data pd.read_excel(data.xlsx) except Exception as e:print(f"Error: {e}")# 假设您的数据包含以下列:…

2024 OceanBase开发者大会:专场论坛亮点抢先看

4 月 20 日&#xff0c;2024 OceanBase 开发者大会将在上海闵行区漕宝路 3199 号宝龙艾美酒店召开。 此次盛会专为开发者们量身打造&#xff0c;OceanBase诚挚邀请了来自不同行业的最佳实践用户、业界享有盛誉的技术专家&#xff0c;以及OceanBase数据库领域的精英们齐聚一堂。…

线程池学习(通俗易懂)

线程池 线程池是什么ThreadPoolExecutor模拟实现线程池结语 线程池是什么 假设我们要频繁的创建线程和销毁线程,但是创建线程和销毁线程是有成本的. 所以我们可以提前创建一批线程,后面需要使用的时候,直接拿就可以了,这就是线程池. 当线程不再使用的时候,就归还到池子里.为什…

软考131-上午题-【软件工程】-软件可靠性、可用性、可维护性

可靠性、可用性和可维护性是软件的质量属性&#xff0c;软件工程中&#xff0c;用 0-1 之间的数来度量。 0.66 66% 1、 可靠性 可靠性是指一个系统对于给定的时间间隔内、在给定条件下无失效运作的概率。 可以用 MTTF/ (1MTTF) 来度量&#xff0c;其中 MTTF 为平均无故障时间…

算法练习第18天|111.二叉树的最小深度

111.二叉树的最小深度 111. 二叉树的最小深度 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/minimum-depth-of-binary-tree/description/ 题目描述&#xff1a; 给定一个二叉树&#xff0c;找出其最小深度。 最小深度是从根节点到最近叶子节点的最…

vue3 vueUse 连接蓝牙

目录 vueuse安装&#xff1a; useBluetooth: 调用蓝牙API 扫描周期设备 选择设备配对 连接成功 vue3的网页项目连接电脑或者手机上的蓝牙设备&#xff0c;使用vueUse库&#xff0c;可以快速检查连接蓝牙设备。 vueUse库使用参考&#xff1a; VueUse工具库 常用api-CSDN…

【代码】Python3|Requests 库怎么继承 Selenium 的 Headers (2024,Chrome)

本文使用的版本&#xff1a; Chrome 124Python 12Selenium 4.19.0 版本过旧可能会出现问题&#xff0c;但只要别差异太大&#xff0c;就可以看本文&#xff0c;因为本文对新老版本都有讲解。 文章目录 1 难点解析和具体思路2 注意事项2.1 PDF 资源获取时注意事项2.2 Capabiliti…

接口防盗刷的方法有哪些?

在工作中&#xff0c;曾经遇到过一个手机号一天发送上百次验证码得情况&#xff0c;这种明显是出问题了&#xff0c;那怎么解决和防范呢&#xff1f; 这是一个非常有意思的问题&#xff0c;防范措施挺多的。今天这篇文章专门跟大家一起聊聊&#xff0c;希望对你会有所帮助。 1…

固定测斜仪:工程观测的精密利器

在工程观测测量领域&#xff0c;固定测斜仪扮演着至关重要的角色。固定测斜仪&#xff0c;凭借其耐冲击型倾斜传感器、出色的可靠性、快速稳定的特点&#xff0c;以及简洁的安装和智能识别功能&#xff0c;已成为行业内重要工具。其输出信号为RS485数字量&#xff0c;可直接显示…

进行接口测试时,连接数据库,对数据源进行备份、还原、验证操作

进行接口测试时&#xff0c;我们需要连接到数据库中&#xff0c;对数据源进行备份、还原、验证等操作。 一、Python连接数据库常见模块 MysqlDBpython2时代最火的驱动库。基于C开发&#xff0c;对windows平台不友好。现在已经进入python3时代&#xff0c;基本不再使用MysqlCl…