二、线程的Thread 类及常见方法【2/12】【多线程】

news2025/6/19 6:01:46

线程的Thread 类及常见方法

  • 2. Thread 类及常见方法
    • 2.1 Thread 的常见构造方法
    • 2.2 Thread 的几个常见属性
    • 2.3 启动一个线程-start()
            • ★★★start和run的区别★★★
            • run不会创建线程 是在原来的基础上 执行代码
            • start 创建线程,在新的线程 执行代码
    • 2.4 中断一个线程
      • ★★★1. 使用自定义的变量来作为标志位
      • ★★★2. Thread.interrupted() 或者Thread.currentThread().isInterrupted() 代替自定义标志位
        • ★3. 标志位的清除
            • Thread.isInterrupted() 和 Tread.interrupted 清楚
            • 而加上 currentThread 表示不清楚
    • 2.5 等待一个线程-join()
            • ★★★哪个线程join 哪个线程先执行
    • 2.6 获取当前线程引用
    • 2.7 休眠当前线程

2. Thread 类及常见方法

Thread 类是 JVM 用来管理线程的一个类,换句话说,每个线程都有一个唯一的 Thread 对象与之关联。
用我们上面的例子来看,每个执行流,也需要有一个对象来描述,类似下图所示,而 Thread 类的对象就是用来描述一个线程执行流的,JVM 会将这些 Thread 对象组织起来,用于线程调度,线程管理
在这里插入图片描述

2.1 Thread 的常见构造方法

在这里插入图片描述在这里插入图片描述
在这里插入图片描述

Thread t1 = new Thread();
Thread t2 = new Thread(new MyRunnable());
Thread t3 = new Thread("这是我的名字");
Thread t4 = new Thread(new MyRunnable(), "这是我的名字");

2.2 Thread 的几个常见属性

属性获取方法
IDgetId()
名称getName()
状态getState()
优先级getPriority()
是否后台线程(主线程执行完便结束)isDaemon()
是否存活isAlive()
是否被中断isInterrupted()

在这里插入图片描述

  1. ID 是线程的唯一标识,不同线程不会重复
  2. 名称是各种调试工具用到
  3. 状态表示线程当前所处的一个情况,下面我们会进一步说明
  4. 优先级高的线程理论上来说更容易被调度到
  5. 关于后台线程,需要记住一点:JVM会在一个进程的所有非后台线程结束后,才会结束运行。
  6. 是否存活,即简单的理解,为 run 方法是否运行结了
  7. 线程的中断问题,下面我们进一步说明
public class ThreadDemo {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    System.out.println(Thread.currentThread().getName() + ": 我还
活着");
                    Thread.sleep(1 * 1000);
               } catch (InterruptedException e) {
                    e.printStackTrace();
               }
           }
            System.out.println(Thread.currentThread().getName() + ": 我即将死去");
       });
        System.out.println(Thread.currentThread().getName() 
                           + ": ID: " + thread.getId());
        System.out.println(Thread.currentThread().getName() 
                           + ": 名称: " + thread.getName());
        System.out.println(Thread.currentThread().getName() 
                           + ": 状态: " + thread.getState());
        System.out.println(Thread.currentThread().getName() 
                           + ": 优先级: " + thread.getPriority());
        System.out.println(Thread.currentThread().getName() 
                           + ": 后台线程: " + thread.isDaemon());
        System.out.println(Thread.currentThread().getName() 
                           + ": 活着: " + thread.isAlive());
        System.out.println(Thread.currentThread().getName() 
                           + ": 被中断: " + thread.isInterrupted());
        thread.start();
        while (thread.isAlive()) {}
        System.out.println(Thread.currentThread().getName() 
                           + ": 状态: " + thread.getState());
   }
}

2.3 启动一个线程-start()

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

  • 覆写 run 方法是提供给线程要做的事情的指令清单
  • 线程对象可以认为是把 李四、王五叫过来了
  • 而调用 start() 方法,就是喊一声:”行动起来!“,线程才真正独立去执行了。
    在这里插入图片描述
    调用 start 方法, 才真的在操作系统的底层创建出一个线程
★★★start和run的区别★★★
run不会创建线程 是在原来的基础上 执行代码
start 创建线程,在新的线程 执行代码

在这里插入图片描述

2.4 中断一个线程

李四一旦进到工作状态,他就会按照行动指南上的步骤去进行工作,不完成是不会结束的。但有时我们需要增加一些机制,例如老板突然来电话了,说转账的对方是个骗子,需要赶紧停止转账,那张三该如何通知李四停止呢?这就涉及到我们的停止线程的方式了。

目前常见的有以下两种方式:

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

★★★1. 使用自定义的变量来作为标志位

示例-1: 使用自定义的变量来作为标志位.

  • 需要给标志位上加 volatile 关键字(这个关键字的功能后面介绍).public volatile boolean isQuit = false;
public class ThreadDemo 
    	private static class MyRunnable implements Runnable {	
        public volatile boolean isQuit = false;
        @Override
        public void run() {
            while (!isQuit) {
                System.out.println(Thread.currentThread().getName()
                        + ": 别管我,我忙着转账呢!");
                try {
                    Thread.sleep(1000);
               } catch (InterruptedException e) {
                    e.printStackTrace();
               }
           }
            System.out.println(Thread.currentThread().getName()
                    + ": 啊!险些误了大事");
       }
   }
    public static void main(String[] args) throws InterruptedException {
        MyRunnable target = new MyRunnable();
        Thread thread = new Thread(target, "李四");
        System.out.println(Thread.currentThread().getName()
                + ": 让李四开始转账。");
        thread.start();
        Thread.sleep(10 * 1000);
        System.out.println(Thread.currentThread().getName()
                + ": 老板来电话了,得赶紧通知李四对方是个骗子!");
        target.isQuit = true;
   }
}

★★★2. Thread.interrupted() 或者Thread.currentThread().isInterrupted() 代替自定义标志位

示例-2: 使用 Thread.interrupted() 或者Thread.currentThread().isInterrupted() 代替自定义标志位.

Thread 内部包含了一个 boolean 类型的变量作为线程是否被中断的标记.

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

什么叫做清除标志位呢?
就是比如看教室里的灯是否关闭了,如果打开了,清除标志位就是,把灯关了,下次再看就是灯是关的,但第一次看的时候,灯是开得。不清除标志位就表示,不关灯,每次看都是灯是开的

使用 thread 对象的 interrupted() 方法通知线程结束.

public class ThreadDemo {
    private static class MyRunnable implements Runnable {
        @Override
        public void run() {
            // 两种方法均可以
            while (!Thread.interrupted()) {
            //while (!Thread.currentThread().isInterrupted()) {
                System.out.println(Thread.currentThread().getName()
                        + ": 别管我,我忙着转账呢!");
                try {
                    Thread.sleep(1000);
               } catch (InterruptedException e) {
                    e.printStackTrace();
                    System.out.println(Thread.currentThread().getName()
                            + ": 有内鬼,终止交易!");
                    // 注意此处的 break
                    break;
               }
           }
            System.out.println(Thread.currentThread().getName()
                    + ": 啊!险些误了大事");
       }
   }
    public static void main(String[] args) throws InterruptedException {
        MyRunnable target = new MyRunnable();
        Thread thread = new Thread(target, "李四");
        System.out.println(Thread.currentThread().getName()
                + ": 让李四开始转账。");
        thread.start();
        Thread.sleep(10 * 1000);
        System.out.println(Thread.currentThread().getName()
                + ": 老板来电话了,得赶紧通知李四对方是个骗子!");
        thread.interrupt();
   }
}

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

  1. 如果线程因为调用 wait/join/sleep 等方法而阻塞挂起,则以 InterruptedException 异常的形式通知,清除中断标志
  • 当出现 InterruptedException 的时候, 要不要结束线程取决于 catch 中代码的写法. 可以选择忽略这个异常, 也可以跳出循环结束线程.
  1. 否则,只是内部的一个中断标志被设置,thread 可以通过
  • Thread.interrupted() 判断当前线程的中断标志被设置,清除中断标志
  • Thread.currentThread().isInterrupted() 判断指定线程的中断标志被设置,不清除中断标志

这种方式通知收到的更及时,即使线程正在 sleep 也可以马上收到。

★3. 标志位的清除

Thread.isInterrupted() 和 Tread.interrupted 清楚
而加上 currentThread 表示不清楚

示例-3 观察标志位是否清除

标志位是否清除, 就类似于一个开关.
Thread.isInterrupted() 相当于按下开关, 开关自动弹起来了. 这个称为 “清除标志位”
Thread.currentThread().isInterrupted() 相当于按下开关之后, 开关弹不起来, 这个称为"不清除标志位".

  • 使用 Thread.isInterrupted() , 线程中断会清除标志位
public class ThreadDemo {
    private static class MyRunnable implements Runnable {
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.interrupted());
           }
       }
   }
    public static void main(String[] args) throws InterruptedException {
        MyRunnable target = new MyRunnable();
        Thread thread = new Thread(target, "李四");
        thread.start();
        thread.interrupt();
   }
}
true // 只有一开始是 true,后边都是 false,因为标志位被清
false
false
false
false
false
false
false
false
false
  • 使用 Thread.currentThread().isInterrupted() , 线程中断标记位不会清除.
public class ThreadDemo {
    private static class MyRunnable implements Runnable {
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
 System.out.println(Thread.currentThread().isInterrupted());
           }
       }
   }
    public static void main(String[] args) throws InterruptedException {
        MyRunnable target = new MyRunnable();
        Thread thread = new Thread(target, "李四");
        thread.start();
        thread.interrupt();
   }
}
true // 全部是 true,因为标志位没有被清
true
true
true
true
true
true
true
true
true

2.5 等待一个线程-join()

★★★哪个线程join 哪个线程先执行

有时,我们需要等待一个线程完成它的工作后,才能进行自己的下一步工作。例如,张三只有等李四转账成功,才决定是否存钱,这时我们需要一个方法明确等待线程的结束

public class ThreadDemo {
    public static void main(String[] args) throws InterruptedException {
        Runnable target = () -> {
            for (int i = 0; i < 10; i++) {
                try {
                    System.out.println(Thread.currentThread().getName() 
                                       + ": 我还在工作!");
                    Thread.sleep(1000);
               } catch (InterruptedException e) {
                    e.printStackTrace();
               }
           }
            System.out.println(Thread.currentThread().getName() + ": 我结束了!");
       };
        Thread thread1 = new Thread(target, "李四");
        Thread thread2 = new Thread(target, "王五");
        System.out.println("先让李四开始工作");
        thread1.start();
        thread1.join();
        System.out.println("李四工作结束了,让王五开始工作");
        thread2.start();
        thread2.join();
        System.out.println("王五工作结束了");
   }
}

join用于让主线程休眠

大家可以试试如果把两个 join 注释掉,现象会是怎么样的呢?

附录

方法说明
public void join()等待线程结束
public void join(long millis)等待线程结束,最多等 millis 毫秒
public void join(long millis, int nanos)同理,但可以更高精度

关于 join 还有一些细节内容,我们留到下面再讲解。

应用:
在这里插入图片描述

2.6 获取当前线程引用

这个方法我们以及非常熟悉了

方法说明
public static Thread currentThread();返回当前线程对象的引用

在这里插入图片描述

public class ThreadDemo {
    public static void main(String[] args) {
        Thread thread = Thread.currentThread();
        System.out.println(thread.getName());
   }
}

2.7 休眠当前线程

也是我们比较熟悉一组方法,有一点要记得,因为线程的调度是不可控的,所以,这个方法只能保证实际休眠时间是大于等于参数设置的休眠时间的

方法说明
public static void sleep(long millis) throws InterruptedException休眠当前线程 millis毫秒
public static void sleep(long millis, int nanos) throws InterruptedException可以更高精度的休眠
public class ThreadDemo {
    public static void main(String[] args) throws InterruptedException {
        System.out.println(System.currentTimeMillis());
        Thread.sleep(3 * 1000);
        System.out.println(System.currentTimeMillis());
   }
}

在这里插入图片描述

在这里插入图片描述

关于 sleep,以后我们还会有一些知识会给大家补充

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

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

相关文章

常见面试题之Redis篇

1.1.Redis与Memcache的区别&#xff1f; redis支持更丰富的数据类型&#xff08;支持更复杂的应用场景&#xff09;&#xff1a;Redis不仅仅支持简单的k/v类型的数据&#xff0c;同时还提供list&#xff0c;set&#xff0c;zset&#xff0c;hash等数据结构的存储。memcache支持…

Open Inventor 2023.1 Crack

发行说明 Open Inventor 2023.1&#xff08;次要版本&#xff09; 文档于 2023 年 4 月发布。 此版本中包含的增强功能和新功能&#xff1a; Open Inventor 10 版本编号更改体积可视化 单一分辨率的体绘制着色器中与裁剪和 ROI 相关的新功能MeshVizXLM 在 C 中扩展的剪辑线提…

拟牛顿法:python代码实现

文章目录拟牛顿法待优化实例scipy工具包实现BFGS自编Python实现BFGS拟牛顿法 在梯度类算法原理&#xff1a;最速下降法、牛顿法和拟牛顿法中&#xff0c;介绍了梯度类算法求解优化问题的设计思路&#xff0c;并以最速下降法、牛顿法和拟牛顿法为例&#xff0c;描述了具体的算法…

3.9、互斥锁(互斥量)

3.9、互斥锁&#xff08;互斥量&#xff09;1.互斥锁&#xff08;互斥量&#xff09;的介绍2. 互斥量相关操作函数3.互斥量函数的使用介绍①pthread_mutex_init②pthread_mutex_destroy③pthread_mutex_lock④pthread_mutex_trylock⑤pthread_mutex_unlock3.利用互斥锁实现线程…

计讯物联双网口工业路由器TR321助力货轮冷链监测解决方案高质量落地

政策背景 国务院办公厅印发我国冷链物联流域第一份五年规划——《“十四五”冷链物流发展规划》&#xff08;以下简称“规划”&#xff09;。《规划》聚焦冷链物流体系、冷链运输、冷链物流服务、冷链物流创新及冷链物流监管体系等方面&#xff0c;对冷链物流的全流程、全环节…

Ficow 的 AI 平台快速上手指南(ChatGPT, NewBing, ChatGLM-6B, cursor.so)

本文首发于 Ficow Shen’s Blog&#xff0c;原文地址&#xff1a; Ficow 的 AI 平台快速上手指南(ChatGPT, NewBing, ChatGLM-6B, cursor.so)。 内容概览 前言OpenAI —— ChatGPT微软 —— NewBing智谱AI —— ChatGLM-6BAI生成代码 —— cursor.so总结 前言 现在各种AI工具大…

虚拟机网络配置

点击【编辑虚拟机设置】&#xff0c;点击【网络适配器】&#xff0c;选择【桥接模式】 选择好之后退回主页&#xff0c;点击【编辑】&#xff0c;选择【虚拟网络编辑器】 添加一个【VMnet8】的网络名称 点击【开启虚拟机】 输入账户密码&#xff0c;输入【cd /etc/sysconfig/ne…

springcloud——gateway功能拓展

目录 1.获取用户真实IP 2.统一跨域配置 3.redis令牌桶算法限流 1.获取用户真实IP 在我们的日常业务中&#xff0c;我们时常需要获取用户的IP地址&#xff0c;作登录日志、访问限制等相关操作。 而在我们的开发架构中&#xff0c;一般我们将服务分为多个微服务&#xff0c;…

熟练了Flex布局之后,该学学Grid布局了

介绍 CSS Gird布局也叫二维网格布局系统&#xff0c;可用于布局页面主要的区域布局或小型组件。网格是一组相交的水平线和垂直线&#xff0c;它定义了网格的列和行。我们可以指定将网格元素放置在与这些行和列相关的位置上。 一维布局 和 二维布局 像流布局和Flex布局&#…

Windows10系统安装perl命令

文章目录1&#xff0c;下载ActivePerl 5.28&#xff08;基于Windows 10系统&#xff09;&#xff1a;1.1&#xff0c;Perl 主页: https://www.perl.org/get.html1.2&#xff0c;选择windows1.3&#xff0c;选择Binaries---activeperla版本1.3&#xff0c;直接选择windows 5.36版…

【观察】神州数码高质量发展背后,是技术创新“叠加效应”的释放

毫无疑问&#xff0c;在百年变局和世纪疫情的双重影响下&#xff0c;整个2022年科技行业的增速都在放缓&#xff0c;更对身处其中的科技企业的业务连续性和成长性提出了更高的考验。尽管如此&#xff0c;神州数码&#xff08;000034.SZ&#xff09;仍然交出了一份令市场亮眼的成…

【iOS逆向与安全】使用ollvm混淆你的源码

前言 当你在研究别人源码的时候&#xff0c;是不是期望着别人代码没有进行任何的防护和混淆。这时的你&#xff0c;是不是应该考虑一下自己代码的安全.本篇文章将告诉你&#xff0c;如何使用ollvm来混淆iOS端的代码【此文为入门贴&#xff0c;大佬请绕道】。 一、目标 编译o…

【MybatisPlus快速入门】—— 拓展入门

逻辑删除 前面我们完成了基本的增删改查操作&#xff0c;但是对于删除操作来说&#xff0c;我们思考一个问题&#xff0c;在实际开发中我们真的会将数据完成从数据库中删除掉么&#xff1f;很多情况下我们是需要保留要删除的数据用来总结统计的&#xff0c;所以我们是不能将数…

从零学习SDK(5)SDK文档的学习和参考

要想充分利用SDK的优势&#xff0c;仅仅下载和安装SDK是不够的&#xff0c;还需要学习和参考SDK提供的文档和资源。文档和资源是SDK的重要组成部分&#xff0c;它们可以帮助开发者掌握SDK的基本概念、结构、用法、限制和最佳实践&#xff0c;以及解决常见的问题和错误。 查找…

(数字图像处理MATLAB+Python)第三章图像基本运算-第二节:图像代数运算

文章目录一&#xff1a;图像算数运算&#xff08;1&#xff09;加法运算A&#xff1a;概述B&#xff1a;程序&#xff08;2&#xff09;减法运算A&#xff1a;概述B&#xff1a;程序&#xff08;3&#xff09;乘法运算A&#xff1a;概述B&#xff1a;程序&#xff08;4&#xf…

C++模板基础(九)

完美转发与 lambda 表达式模板 void f(int& input) {std::cout << "void f(int& input)\t" << input << \n; }void f(int&& input) {std::cout << "void f(int&& input)\t" << input << \n;…

MYSQL8窗口函数

MYSQL8窗口函数MYSQL8窗口函数窗口函数分类序号函数--排行榜row_number()示例rank()示例dense_rank()示例partition by对每个分区内的行进行排名不加partition by全局排序开窗聚合函数分布函数CUME_DIST()PERCENT_RANK()前后函数LAG()的用法LEAD()头尾函数其他函数NTH_VALUE()N…

用Abp实现两步验证(Two-Factor Authentication,2FA)登录(二):Vue网页端开发

文章目录发送验证码登录退出登录界面控件获取用户信息功能项目地址前端代码的框架采用vue.js elementUI 这套较为简单的方式实现&#xff0c;以及typescript语法更方便阅读。首先添加全局对象&#xff1a; loginForm: 登录表单对象 twoFactorData: 两步验证数据&#xff0c; …

跨平台应用开发进阶(五十九):uni-app实现视屏播放小窗功能

文章目录一、前言二、解决方案三、拓展阅读一、前言 在业务功能开发过程中&#xff0c;需要实现视频直播、播放小窗功能。鉴于目前通过接入火山webSDK实现视频直播、点播功能。需要火山协助配合改造实现小窗功能。 uni-app插件市场也提供了若干插件&#xff0c;经试用效果并不…

从二叉树的角度看快速排序

快速排序本质上可以看作二叉树的前序遍历 快速排序是先将一个元素排好序&#xff0c;然后再将剩下的元素排好序 核心思路依然是分治 快排整体思路 准确的可以说是治分 > 先治 得到分界点后 再分 治&#xff1a;双指针技巧&#xff08;左右指针或者快慢指针&#xff0c;…