Thread类以及常见方法

news2025/3/18 15:22:19

Thread类是JVM用来管理线程的一个类,每个线程都有一个唯一的Thread对象与之关联。

多一个线程,就多一条执行流,每个执行流也要一个对象来描述,而Thread类的对象就是用来描述一个线程的执行流,JVM 会将这些 Thread 对象组织起来,⽤于线程调度,线程管理

Thread常见的构造方法:

1.Thread t1=new Thread();
2.Thread t2=new Thread(()->{});
3.Thread t3=new Thread("这是我的名字t3");
4.Thread t4=new Thread(()->{},"这是我的名字t4");

Thread的常见属性:

ID:

可以通getId()方法来获取,就相当于是线程的身份标识符,线程的唯一标识,不同线程不会重复。在java中,不同线程的id是由java虚拟机来分配的

public class test1 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1=new Thread(()->{});
        Thread t2=new Thread(()->{});
        Thread t3=new Thread(()->{});
        t1.start();
        t2.start();
        t3.start();
        Thread.sleep(1000);
        System.out.println("t1的ID"+t1.getId()+" t2的ID"+t2.getId()+" t3的ID"+t3.getId());
        System.out.println("main线程结束....");
    }
}

运行结果:

 可以看出不同的线程,ID不一样。

名称:

各种调试工具会用到。(名称可以随便取,只是方便用于调试)

public class test1 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1=new Thread(()->{});
        Thread t2=new Thread(()->{});
        Thread t3=new Thread(()->{});
        t1.start();
        t2.start();
        t3.start();
        Thread.sleep(1000);
        System.out.println("t1的name:"+t1.getName()+" t2的name:"+t2.getName()+" t3的name:"+t3.getName());
        System.out.println("main线程结束....");
    }
}

运行结果:

 由此可知:当用户没有去手动命名,JVM会自动命名,且命名格式如上:Thread-0;Thread-1.....

线程的状态:

表示当时线程处于一个的情况。(下面会详细说明)

优先级:

在java中,线程优先级是用来帮助操作系统决定哪个线程先获取CPU资源的一个机制。决定那个线程先获取CPU资源的一个机制。

线程的优先级是一个整数:范围从1-10(Thread.MIN_PRIORITY-Thread.MAX_PRIORITY)

默认优先级是5(Thread.NORM_PRIORITY).

我们可以通过setPriority()方法来设置线程的优先级:thread.setPriority(7)

线程优先级只是一种提示,不能保证优先级越高就越先执行,因为实际的线程调度依赖于底层的操作系统的策略和当前的负载情况。

优先级更高的线程理论上更容易被调用到。

isDaemon:

JVM会在一个进程中的所有非后台线程结束后,才会结束执行。

isDaemon是否是后台线程(守护线程)

public class test1 {
    public static void main(String[] args) throws InterruptedException {
       Thread t1=new Thread(()->{
           while(true){
               System.out.println("t1正在执行....");
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
       });
        Thread t2=new Thread(()->{
            while(true){
                System.out.println("t2正在执行....");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        Thread t3=new Thread(()->{
            while(true){
                System.out.println("t3正在执行....");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t1.start();
        t2.start();
        t3.start();
        Thread.sleep(1000);
        System.out.println("main线程结束....");
    }
}

 在上面这个测试类中,有t1,t2,t3,main主线程(四个线程)

运行结果:

前台线程:

我们会发现,虽然main线程结束了,但是进程依然还在继续执行。因为t1,t2,t3,main,这四个线程都是前台线程(能影响到进程存在的线程叫“前台线程”),进程只会等所有的前台进程都结束了才结束进程。

自己代码创建的线程,包括main主线程,默认都是前台线程,可以通过setDaemon方法来修改。

后台线程:

JVM提供这些线程属于有特殊功能的线程,并且伴随的整个进程持续执行的,比如:垃圾回收线程(伴随着整个进程的后台线程)

当我们把上面的t1,t2,t3这三个线程修改为后台进程。再看看运行结果:

        t1.setDaemon(true);
        t2.setDaemon(true);
        t3.setDaemon(true);

 运行结果:

当唯一的前台线程main(主线程)一结束,整个进程都结束了。 

是否存活isAlive():

前面提到,Thread类是JVM用来管理一个线程的,每一个线程都有一个Thread对象与之关联。isAlive方法就是返回线程的生命周期,而非是Thread对象的生命周期,当线程中的run()还在运行时返回true,若run()运行结束就返回false。

但是Thread对象的生命周期和系统中的线程的生命周期,是不同的。

Thread对象存在,但是系统中的线程已经销毁(线程的run方法结束了)的情况:比如像下面代码这种:

public class test1 {
    public static void main(String[] args) throws InterruptedException {
       Thread t=new Thread(()->{
           for (int i = 0; i < 3; i++) {
               System.out.println("hello thread");
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
       });

        t.start();
        while(true){
            System.out.println(t.isAlive());
            Thread.sleep(1000);
        }
    }
}

 运行结果:

这个代码理应三秒后就结束了,但是我们看运行结果:虽然存在了3秒,但是打印了四个true(这里的结果可能是不同的,因为操作系统的随机调度),解释:主线程的第四次打印isAlivet线程结束(因为随机调度),谁先谁后,不一定,所以第四次打印true,说明t线程还剩“最后一口气”

 中断一个线程Interrupt()方法:

在Java中,通过用Interrupt方法来中断线程(这里的中断,是直接让线程停止了,不会再恢复),这个方法会向线程发送一个中断信号,但是线程具体行为取决于代码的具体实现,常见的方法就是在run()方法中使用一个循环(isInterrupted()方法)来检测线程中断状态,并在接收到中断信号并退出循环,从而终止线程的执行。(在isinterrupted方法内部有一个标志位,若接收到来自interrupt的中断信号,这个标志位就会修改为true

public class test1 {
    public static void main(String[] args) throws InterruptedException {
     Thread t=new Thread(()->{
        while(!Thread.currentThread().isInterrupted()){//判断线程是否终止
            System.out.println("hello thread");
        }
     });
     t.start();
     t.interrupt();//主动线程中断
    }
}

这里就是在run()方法里循环判断线程的状态,用来判断线程是否被终止。

Thread.currentThread()是一个静态方法,此时就是那个线程调用,获取到的就是那个线程的Thread引用。

 为什么这里不能直接用t,因为lambda这里的定义是在new Thread之前,也是在Thread t声明之前。

主动的去进行终止,Java中的Thread类中有一个boolean类型的变量叫做interrupted flag(中断标志),它用来表示线程是否被中断。调用线程的interrupt()方法会将这个标志设置为true,表示线程被中断。上面这个代码就是修改了这个flag这个变量的值,除此之外还能唤醒sleep这样的阻塞。

变量捕获: 

先看一段代码:

public class test1 {
    public static void main(String[] args) throws InterruptedException {
        boolean isFinished = false;//局部变量
        Thread t = new Thread(() -> {
            while (!isFinished) {
                System.out.println("hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            System.out.println("thread 结束");
        });
        t.start();
        Thread.sleep(3000);
        isFinished = true;//修改这个变量
    }
}

 这里会报红,为什么呢?

在Lambda表达式中希望使用外面的变量,就会触发“变量捕获”这样的语法。

Lambda是回调函数,执行的时候是很久之后(操作系统真正创建出线程之后,才会执行),后面很有可能,后续线程创建好了,当前main这里的方法都执行完了,对应的isFinished局部变量就销毁了。(也就是生命周期失效了)

为了解决上述问题:java的做法,把被捕获的变量会拷贝一份,拷贝给lambda里面

外面的变量是否销毁,都不会影响lambda里面的执行了。

拷贝意味着,这样的变量就不适合修改。

修改一方,另一方不会随之修改(因为本质上是两个变量),后续就会带来更多的困惑。

后面又想出一个办法:通过final限制,压根就不让你修改这个变量。

这样注释掉这个修该之后

就不会报红了。

再看下面一个代码:

public class test1 {
    private static boolean isFinished = false;//成员变量
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            while (!isFinished) {
                System.out.println("hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            System.out.println("thread 结束");
        });
        t.start();
        Thread.sleep(3000);
        isFinished = true;
    }
}

 lambda本质上是一个函数式接口,相当于一个内部类,isFinished变量本身就是一个外部类(test1)的成员

内部类本来就可以访问外部类的成员(成员变量生命周期是由GC来管理的),所以在lambda里面不担心变量周期失效的问题,也不必通过“拷贝”,也不必限制final

interrupt唤醒sleep:

public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            while (!Thread.currentThread().isInterrupted()) {
                System.out.println("thread继续执行");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    System.out.println("sleep抛出异常");
                    break;//使用break结束循环
                }
            }
        });
        t.start();
        Thread.sleep(3000);
        System.out.println("main尝试尝试终止线程");
        t.interrupt();
    }

 每次循环,绝大部分时间都是sleep。这时主线程调用interrupt,这个操作不仅能够唤醒sleep,还能让sleep抛出异常

再看一个代码:

public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            while (!Thread.currentThread().isInterrupted()) {
                System.out.println("thread继续执行");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    //不做处理
                }
            }
        });
        t.start();
        Thread.sleep(3000);
        System.out.println("main尝试终止线程");
        t.interrupt();
    }

 运行结果:

正常来说,调用interrupt方法后,会将isterrupted中的标准位改为true,然后终止循环。但是结果发现,循环没有结束

因为上述代码中,将sleep唤醒了,然后sleep在被唤醒之后,把isterrupted标志位修改为false,这样进行循环条件判断,就会继续执行。

sleep这样设计的,就可以让程序员在catch语句中有更多的选择:线程是否立即结束,还是等会结束,还是不结束(忽略这个终止信号)

java中的线程终止,不是一个“强制性”的,选择权在程序员自己手上。

等待一个线程-join()方法:

多个线程之间,采用并发执行和随机调度。但是程序员并不喜欢随机的东西。

join作用:确定多个线程之间,结束的先后顺序。如果有一个线程t,然后在主线程中调用t.join(),意思就是让主线程等待t线程结束再执行

 两张图对应看。在main线程中,调用t.join()效果,让main线程等待t先结束(当执行到t.join时,此时main线程就会阻塞等待,一直阻塞到t线程执行完,main主线程才能继续执行)

但是这个方法不是很好,假如t线程发生了什么意外,难道主线程要一直等下去吗?这就不切实际了。谁愿意一直去等一个等不到的人呢?

所以join提供了带参数的版本,可以指定join等待的时候(超时时间-等待的最大时间)

t.join(3000);

带有超时时间的等待,才是更加科学的做法;计算机中,尤其是与通信相关的逻辑,一般都需要“超时时间”

t.join(3000,500);

join方法也提供了上面这个版本,3000的单位是毫秒,而500的单位是纳秒(一般不使用

1s=1000ms

1ms=1000us

1us=1000ns

 休眠当前线程:

在java中,可以使用Thread.sleep()方法来休眠当前线程,前面也提到当使用sleep方法时,可能抛出异常,需要进行相应的处理(异常捕获),代码如下:

try{
    Thread.sleep(时间毫秒数);
}catch(InterruptedException e){
    e.printStackTrace();
}

当代码调用sleep,相当于让当前线程,让出cpu资源,后续时间到了,需要操作系统内核了,把这个线程重新调到cpu上,才能继续执行。所以当调用sleep(1000)这个方法,真的是1000毫秒吗?实际上比1000毫秒要多一点,时间到了,并不意味着立即就执行了,而是可以被调度了

sleep(0)是sleep方法的一种特殊写法,当调用了sleep(0),意味着让当前的线程立即放弃cpu资源(放弃cpu资源,把cpu让出来给更多的人执行机会),等待操作系统重新调度

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

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

相关文章

【蓝桥杯—单片机】第十一届省赛真题代码题解题笔记 | 省赛 | 真题 | 代码题 | 刷题 | 笔记

第十一届省赛真题代码部分 前言赛题代码思路笔记竞赛板配置内部振荡器频率设定键盘工作模式跳线扩展方式跳线 建立模板明确设计要求和初始状态显示功能部分数据界面第一部分第二部分第三部分调试时发现的问题 参数设置界面第一部分第二部分和第四部分第三部分和第五部分 按键功…

CLion2024.3.2版中引入vector头文件报错

报错如下&#xff1a; 在MacBook端的CLion中引入#include <vector>报 vector file not found&#xff08;引入map、set等也看参考此方案&#xff09;&#xff0c;首先可以在Settings -> Build,Execution,Deployment -> Toolchains中修改C compiler和C compiler的路…

自动化测试工具:selenium

&#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 Selenium是一个用于Web应用程序测试的工具。是一个开源的Web的自动化测试工具&#xff0c;最初是为网站自动化测试而开发的&#xff0c;类型像我们玩游戏用的按键…

MR30分布式IO模块:驱动智能制造工厂的工业互联与高效控制新范式

在工业4.0与智能制造浪潮的推动下&#xff0c;传统制造业正经历着从“机械驱动”向“数据驱动”的深刻转型。作为工业数据连接领域的领军者&#xff0c;明达技术凭借其自主研发的MR30分布式IO模块&#xff0c;以创新的技术架构与卓越的性能表现&#xff0c;为全球制造企业构建了…

计算机领域QPM、TPM分别是什么并发指标,还有其他类似指标吗?

在计算机领域&#xff0c;QPM和TPM是两种不同的并发指标&#xff0c;它们分别用于衡量系统处理请求的能力和吞吐量。 QPM&#xff08;每分钟请求数&#xff09; QPM&#xff08;Query Per Minute&#xff09;表示每分钟系统能够处理的请求数量。它通常用于衡量系统在单位时间…

Python----Python高级(并发编程:协程Coroutines,事件循环,Task对象,协程间通信,协程同步,将协程分布到线程池/进程池中)

一、协程 1.1、协程 协程&#xff0c;Coroutines&#xff0c;也叫作纤程(Fiber) 协程&#xff0c;全称是“协同程序”&#xff0c;用来实现任务协作。是一种在线程中&#xff0c;比线程更加轻量级的存在&#xff0c;由程序员自己写程序来管理。 当出现IO阻塞时&#xff0c;…

DeepSeek使用技巧大全(含本地部署教程)

在人工智能技术日新月异的今天&#xff0c;DeepSeek 作为一款极具创新性和实用性的 AI&#xff0c;在众多同类产品中崭露头角&#xff0c;凭借其卓越的性能和丰富的功能&#xff0c;吸引了大量用户的关注。 DeepSeek 是一款由国内顶尖团队研发的人工智能&#xff0c;它基于先进…

ElasticSearch集群因索引关闭重打开导致飘红问题排查

背景 某组件向 ElasticSearch 写入数据&#xff0c;从最近某一天开始写入速度变慢&#xff0c;数据一直有积压。推测是 ElasticSearch 集群压力导致的&#xff0c;查看 ElasticSearch 集群状态&#xff0c;发现集群确实处于 red 状态。 本文记录 ElasticSearch 集群因索引关闭…

计算机毕业设计Tensorflow+LSTM空气质量监测及预测系统 天气预测系统 Spark Hadoop 深度学习 机器学习 人工智能

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

手搓基于CNN的Chest X-ray图像分类

数据集Chest X-ray PD Dataset 数据集介绍 - 知乎https://zhuanlan.zhihu.com/p/661311561 CPU版本 import torch import torch.nn as nn import torch.optim as optim from torch.utils.data import Dataset, DataLoader from torchvision import transforms, models import …

使用java代码操作rabbitMQ收发消息

SpringAMQP 将来我们开发业务功能的时候&#xff0c;肯定不会在控制台收发消息&#xff0c;而是应该基于编程的方式。由于RabbitMQ采用了AMQP协议&#xff0c;因此它具备跨语言的特性。任何语言只要遵循AMQP协议收发消息&#xff0c;都可以与RabbitMQ交互。并且RabbitMQ官方也…

【数据结构】(7) 栈和队列

一、栈 Stack 1、什么是栈 栈是一种特殊的线性表&#xff0c;它只能在固定的一端&#xff08;栈顶&#xff09;进行出栈、压栈操作&#xff0c;具有后进先出的特点。 2、栈概念的例题 答案为 C&#xff0c;以C为例进行讲解&#xff1a; 第一个出栈的是3&#xff0c;那么 1、…

Composo:企业级AI应用的质量守门员

在当今快速发展的科技世界中,人工智能(AI)的应用已渗透到各行各业。然而,随着AI技术的普及,如何确保其可靠性和一致性成为了企业面临的一大挑战。Composo作为一家致力于为企业提供精准AI评估服务的初创公司,通过无代码和API双模式,帮助企业监测大型语言模型(LLM)驱动的…

Python数据分析案例71——基于十种模型的信用违约预测实战

背景 好久没写这种基础的做机器学习流程了&#xff0c;最近过完年感觉自己代码忘了好多.....复习一下。 本次带来的是信贷违约的预测&#xff0c;即根据这个人的特征&#xff08;年龄收入什么的&#xff09;&#xff0c;预测他是不是会违约&#xff0c;会违约就拒绝贷款&…

python康威生命游戏的图形化界面实现

康威生命游戏&#xff08;Conway’s Game of Life&#xff09;是由英国数学家约翰何顿康威&#xff08;John Horton Conway&#xff09;在1970年发明的一款零玩家的细胞自动机模拟游戏。尽管它的名字中有“游戏”&#xff0c;但实际上它并不需要玩家参与操作&#xff0c;而是通…

区块链技术:Facebook 重塑社交媒体信任的新篇章

在这个信息爆炸的时代&#xff0c;社交媒体已经成为我们生活中不可或缺的一部分。然而&#xff0c;随着社交平台的快速发展&#xff0c;隐私泄露、数据滥用和虚假信息等问题也日益凸显。这些问题的核心在于传统社交媒体依赖于中心化服务器存储和管理用户数据&#xff0c;这种模…

UE求职Demo开发日志#25 试试网络同步和尝试打包

1 改了一些时序上的bug&#xff0c;成功运行了多端 &#xff08;UE一些网络相关的功能都弄好了&#xff0c;只需要标记哪个变量或Actor需要复制&#xff09; 2 以前遗留的bug太多了&#xff0c;改到晚上才打包好一个能跑的版本&#xff0c;而且有的特效还不显示&#xff08;悲…

Win10环境使用ChatBox集成Deep Seek解锁更多玩法

Win10环境使用ChatBox集成Deep Seek解锁更多玩法 前言 之前部署了14b的Deep Seek小模型&#xff0c;已经验证了命令行及接口方式的可行性。但是纯命令行或者PostMan方式调用接口显然不是那么友好&#xff1a; https://lizhiyong.blog.csdn.net/article/details/145505686 纯…

第 26 场 蓝桥入门赛

2.对联【算法赛】 - 蓝桥云课 问题描述 大年三十&#xff0c;小蓝和爷爷一起贴对联。爷爷拿出了两副对联&#xff0c;每副对联都由 N 个“福”字组成&#xff0c;每个“福”字要么是正的&#xff08;用 1 表示&#xff09;&#xff0c;要么是倒的&#xff08;用 0 表示&#…

CodeGPT + IDEA + DeepSeek,在IDEA中引入DeepSeek实现AI智能开发

CodeGPT IDEA DeepSeek&#xff0c;在IDEA中引入DeepSeek 版本说明 建议和我使用相同版本&#xff0c;实测2022版IDEA无法获取到CodeGPT最新版插件。&#xff08;在IDEA自带插件市场中搜不到&#xff0c;可以去官网搜索最新版本&#xff09; ToolsVersionIntelliJ IDEA202…