<JavaEE> Thread线程类 和 Thread的常用方法

目录

一、Thread概述

二、构造方法

三、常用方法

1.1 getId()、getName()、getState()、getPririty()

1.2 start()

1.3 isDaemon()、setDaemon()

1.4 isAlive()

1.5 currentThread()

1.6 Interrupt()、interrupted()、isInterrupted()

1.6.1 方法一:添加共享的标志位

1.6.2  方法二:使用内置的标志位

1.6.3 Java中终止线程不是强制性的

1.7 sleep()

1.8 join()


一、Thread概述

        Thread类是JVM用于管理线程的类,每一个线程都与一个唯一的Thread对象相关联,即每个执行流都由一个Thread对象进行描述,这些对象被JVM组织,用于线程调度和管理。

二、构造方法

构造方法说明
Thread()创建线程对象
Thread(Runnable target)使用Runnable接口实现类对象,创建线程对象
Thread(String name)创建线程对象,并为线程对象命名
Thread(Runnable target, String name)使用Runnable接口实现类对象,创建线程对象,并为线程对象命名
Thread(TreadGroup group, Runnable target)指定线程组,使用Runnable接口实现类对象,创建线程对象

三、常用方法

常用方法说明
getId()获取线程ID
getName()获取线程名
getState()获取线程状态
getPririty()获取线程优先级
start()启动线程
isDaemon()判断线程是否为后台线程(守护线程)
setDaemon()设定线程是否为后台线程(守护线程)
isAlive()判断线程是否“存活”
currentThread()获取当前线程的引用
Interrupt()终止一个线程
interrupted()判断当前线程标志位状态
isInterrupted()判断对象线程标志位状态
sleep()休眠线程
join()阻塞线程

1.1 getId()、getName()、getState()、getPririty()

getId()
获取线程ID,ID是线程的唯一标识,由JVM自动分配并确保唯一性。
getName()
获取线程名,线程名可以自动生成,也可以自定义。线程名可以重复
getState()
获取线程状态,线程的状态有就绪、阻塞等。Java中现成的状态使用枚举保存,可以通过遍历枚举获得所有状态的描述

阅读指针 -> 《Java中线程有多少种状态(State)?状态之间的关系有什么关系?》

<JavaEE> Java中线程有多少种状态(State)?状态之间的关系有什么关系?-CSDN博客文章浏览阅读3次。介绍Java中的线程状态和状态之间的关系有什么关系。https://blog.csdn.net/zzy734437202/article/details/134626843

getPririty()
获取线程优先级,优先级高的线程理论上更容易被调度使用,但是在Java中优先级的效果并不明显。

操作演示:

class MyThread extends Thread{
    @Override
    public void run() {
        for(int i=0;i<5;i++){
            System.out.println("这是MyThread");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public class Thread_Demo6 {
    public static void main(String[] args){
        Thread thread = new MyThread();
        System.out.println("ID:"+thread.getId());
        System.out.println("线程名:"+thread.getName());
        System.out.println("状态:"+thread.getState());
        System.out.println("优先级:"+thread.getPriority());
    }
}

打印结果:

ID:20
线程名:Thread-0
状态:NEW
优先级:5

1.2 start()

start() 启动线程
1)通过重写Thread中的run方法可以创建一个线程对象,再通过调用start()方法,启动这个线程。此时,操作系统中的线程才真正被创建出来。
2)Thread调用start()创建出的线程,底层仍然是调用系统的API来进行创建线程的操作
3)Thread类使用start方法启动线程,对于同一个Thread对象,start方法只能调用一次,需要启动多少个线程,就需要创建多少个Thread对象。
4)start()和run()的区别在于,run方法是提供了线程需要运行的内容,而start方法才是真正让线程运行起来。

同一个Thread对象不能多次调用start方法演示:

class MyThread extends Thread{
    @Override
    public void run() {
        for(int i=0;i<2;i++){
            System.out.println("这是MyThread");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("MyThread-run()运行结束");
    }
}
public class Thread_Demo9 {
    public static void main(String[] args) {
        Thread thread = new MyThread();

        //第一次启动线程thread;
        thread.start();

        //第二次启动线程thread;
        thread.start();
    }
}

打印结果:

Exception in thread "main" java.lang.IllegalThreadStateException
    at java.lang.Thread.start(Thread.java:708)
    at Learn_Thread.Demo9.Thread_Demo9.main(Thread_Demo9.java:32)

这是MyThread
这是MyThread
MyThread-run()运行结束


可以看到两次调用start方法,只有一次成功执行,另一次报错IllegalThreadStateException

1.3 isDaemon()、setDaemon()

isDaemon() 判断线程是否为后台线程
setDaemon() 设定线程是否为后台线程
1)daemon的意思是守护,因此也将后台线程称为守护线程。与后台线程相呼应,还有前台线程。
2)代码创建的线程,默认为前台线程。当setDaemon()方法的参数为false时,线程将被设置为前台线程,当参数为true时,线程将被设置为后台线程。
3)前台线程的运行时,将阻止进程结束;后台线程运行时,不会阻止进程结束。
4)因此为什么将后台线程称为守护线程?就是说进程需要我就在,进程不要我就走,在背后默默守护进程的线程嘛???

后台进程的执行演示:

class MyThread extends Thread{
    @Override
    public void run() {
        for(int i=0;i<5;i++){
            System.out.println("这是MyThread");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public class Thread_Demo7 {
    public static void main(String[] args) {
        Thread thread = new MyThread();

        //设置为守护线程(后台线程);
        thread.setDaemon(true);
        //开始thread;
        thread.start();

        //main线程等待两秒后结束,此时守护线程还有代码没打印,但也随之结束了;
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("main线程结束");
    }
}

打印结果:

这是MyThread
这是MyThread
main线程结束


启动main线程和thread线程,在main线程sleep两秒结束线程后,可以看到thread线程的run()方法打印结果确实不是预期中的五次“这是MyThread”。这意味着thread线程也随着main线程的结束而结束了。

1.4 isAlive()

isAlive() 判断线程是否“存活”
1)Java中的线程类Thread对象实例,虽然表示一个线程,但这个实例的生命周期与系统内核中的线程的生命周期是不同的。
2)Thread对象创建了就存在,但此时如果调用isAlive()得到的结果将会是false,因为内核中的线程此时还不存在。
3)只有当调用start()启动线程之后,内核中的线程启动,调用isAlive()得到的结果才会是true。
4)当线程运行结束,系统内核中的线程也随之结束,此时虽然Thread对象还存在,但是调用isAlive()得到的结果也将是false。

5)因此,可以简单将这个方法认为是用于判断系统内核中的线程(PCB)是否存在。

从代码层面来讲,可以认为是用于判断run()方法是否执行完毕。

演示判断线程是否“存活”:

class MyThread extends Thread{
    @Override
    public void run() {
        for(int i=0;i<2;i++){
            System.out.println("这是MyThread");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("MyThread-run()运行结束");
    }
}
public class Thread_Demo8 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new MyThread();

        //线程已有实例,判断isAlive();
        System.out.println("start前,thread是否存活:"+thread.isAlive());

        //启动线程,判断isAlive();
        thread.start();
        System.out.println("start中,thread是否存活:"+thread.isAlive());

        //等待run()执行结束,此时实例依旧存在,判断isAlive();
        Thread.sleep(5000);
        System.out.println("start结束,thread是否存活:"+thread.isAlive());
    }
}

打印结果:

start前,thread是否存活:false
start中,thread是否存活:true

这是MyThread
这是MyThread
MyThread-run()运行结束
start结束,thread是否存活:false

1.5 currentThread()

currentThread() 获取当前线程的引用
1)在currentThread()方法返回的打印信息中,有三个值,分别代表[线程名,线程优先级,所在线程组]。

获取线程引用操作演示:

public class Thread_Demo11 {
    public static void main(String[] args) {
        //打印main线程信息;
        System.out.println(Thread.currentThread());
    }
}

 打印结果:

Thread[main,5,main]


中括号中的三个值分别代表:线程名,线程优先级,所在线程组。

1.6 Interrupt()、interrupted()、isInterrupted()

如果想要中断(终止)一个线程,可以有多种方法,以下介绍两种:

方法一:通过共享的标志进行线程间沟通。
方法二:调用Interrupt()方法。

1.6.1 方法一:添加共享的标志位

class MyThread extends Thread{
    //设置共享的标志位;
    public volatile boolean isQuit = false;
    @Override
    public void run() {
        //根据标志位的变化,决定后续执行;
        while (!isQuit){
            System.out.println("这是MyThread");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("MyThread-run()运行结束");
    }
}
public class Thread_Demo10 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new MyThread();
        //启动线程;
        thread.start();

        //延时两秒后更改标志位;
        Thread.sleep(2000);
        ((MyThread) thread).isQuit = true;
    }
}

打印结果:

这是MyThread
这是MyThread
这是MyThread
MyThread-run()运行结束

1.6.2  方法二:使用内置的标志位

方法说明
public void interrupt()

如果线程处于阻塞状态,则抛出异常;

如果线程不处于阻塞状态,则终止线程。

public static boolean interrupted()判断当前线程标志位状态。
public boolean isInterrupted()判断对象线程标志位状态。
1)上文由程序员手动设置了一个共享的标志位,用于控制线程的执行。Java中也提供了相应的封装好的方法,内置了标志位。使用以上三个方法,一样可以达到控制线程的执行效果。

通过Interrupt()终止线程,然后对线程标志位状态进行判断:

class MyThread extends Thread{
    @Override
    public void run() {
        //使用isInterrupted()判断标志位状态;
        while (!Thread.currentThread().isInterrupted()){
            System.out.println("这是MyThread");

            //打印当前线程标志位的状态;
            System.out.println(Thread.interrupted());
        }
        System.out.println("MyThread-run()运行结束");
    }
}
public class Thread_Demo12 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new MyThread();
        thread.start();

        //延时两秒后更改标志位;
        Thread.sleep(2000);
        thread.interrupt();
    }
}

打印结果: 

......

这是MyThread
false
这是MyThread
false
这是MyThread
false
MyThread-run()运行结束


休眠两秒后,thread通过调用interrupt()方法修改了标志位,线程终止执行。

当更改标志位,但线程处于阻塞状态时:

        在更改标志位时,如果线程因为调用 wait/join/sleep 等方法而阻塞,则此时会抛出异常InterruptedException,并重置终止标志位。此时程序的后续执行通过catch子句中的异常处理方案来决定。
        如果在更改标志位时,线程非为阻塞状态,则标志位不会重置,可以通过interrupted()或isInterrupted()进行判断。
class MyThread extends Thread{
    @Override
    public void run() {
        while (!Thread.currentThread().isInterrupted()){
            System.out.println("这是MyThread");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("MyThread-run()运行结束");
    }
}
public class Thread_Demo14 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new MyThread();
        thread.start();

        //等待两秒后唤醒;
        Thread.sleep(2000);
        thread.interrupt();
    }
}

打印结果:

这是MyThread
这是MyThread
java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    at Learn_Thread.Demo14.MyThread.run(Thread_Demo14.java:16)

这是MyThread
这是MyThread
这是MyThread


可以看到,在抛出异常后,仍然继续打印,这意味着原先由interrupt()方法修改的标志位,在sleep唤醒时,又被重置为false了。

在异常处理中,加入更多的处理方法:

class MyThread extends Thread{
    @Override
    public void run() {
        while (!Thread.currentThread().isInterrupted()){
            System.out.println("这是MyThread");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
                //在异常处理中加入break,跳出循环;
                break;
            }
        }
        System.out.println("MyThread-run()运行结束");
    }
}
public class Thread_Demo14 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new MyThread();
        thread.start();

        //等待两秒后唤醒;
        Thread.sleep(2000);
        thread.interrupt();
    }
}

打印结果:

这是MyThread
这是MyThread
java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    at Learn_Thread.Demo14.MyThread.run(Thread_Demo14.java:16)

MyThread-run()运行结束


线程通过interrupt()方法修改了标志位,但由于线程此时大概率处于sleep(即,阻塞状态),因此,抛出异常,并将标志位重置。

这里线程会终止,是由异常处理中的break跳出循环得到的结果。

1.6.3 Java中终止线程不是强制性的

操作系统中的API:提供了强制终止线程的操作,无论线程执行到何种程度,都强行结束线程。
Java中的的API:终止线程需要对应线程互相配合,而不是直接“剪断”。
优劣:强制结束线程的方式更“随心所欲,为所欲为”,但如果线程执行过程中被强行终止,可能导致出现一些临时性质的“错误”数据。而相互配合的线程终止,虽然使终止线程时需要考虑的事情变多了,但也使得线程的终止更“安全”,系统运行更稳定了

1.7 sleep()

方法说明
public static void sleep(long millis) throws InterruptedException

以毫秒级别的精度,指定休眠时间

public static void sleep(long millis, int nanos) throws InterruptedException以纳秒级别的精度,指定休眠时间
1)sleep()方法只能保证实际休眠时间大于等于参数设置的休眠时间。
2)sleep被提前唤醒(如被上文的interrup唤醒)时,会抛出异常,并将Thread对象的标志位重置为false。

sleep被提前唤醒时,为什么要重置标志位?

sleep重置标志位,可以给程序员更多的“可操作空间”。

通过抛出异常,处理异常,程序的后续执行可以且不仅可以让线程立即结束,增加了代码的灵活性。

1.8 join()

join() 等待线程结束
join(long millis) 等待线程结束,指定最长等待时间
join(long millis, int nanos) 等待线程结束,以纳秒级别的精度指定最长等待时间
1)由于随即调度,抢占式执行,多线程的执行顺序是不确定的。但是通过应用程序中的API,可以影响到线程的执行顺序。
class MyThread extends Thread{
    @Override
    public void run() {
        for(int i=0;i<5;i++){
            System.out.println("这是MyThread");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("MyThread-run()运行结束");
    }
}
public class Thread_Demo13 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new MyThread();
        thread.start();

        //让主线程阻塞,保证MyThread先运行完;
        thread.join();
        System.out.println("在MyThread结束后打印");
    }
}

打印结果:

这是MyThread
这是MyThread
这是MyThread
这是MyThread
这是MyThread
MyThread-run()运行结束
在MyThread结束后打印


可以看到,main线程中的“在MyThread结束后打印”确实是在thread线程结束后才打印的。


阅读指针 -> 《线程安全(重点!!)》

链接生成中..........

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

S25FL系列FLASH读写的FPGA实现

文章目录 实现思路具体实现子模块实现top模块 测试Something 实现思路 建议读者先对 S25FL-S 系列 FLASH 进行了解&#xff0c;我之前的博文中有详细介绍。 笔者的芯片具体型号为 S25FL256SAGNFI00&#xff0c;存储容量 256Mb&#xff0c;增强高性能 EHPLC&#xff0c;4KB 与 6…

Java中static、final、static final的区别

文章目录 finalstaticstatic final final final可以修饰&#xff1a;属性&#xff0c;方法&#xff0c;类&#xff0c;局部变量&#xff08;方法中的变量&#xff09; final修饰的属性的初始化可以在编译期&#xff0c;也可以在运行期&#xff0c;初始化后不能被改变。 final修…

nginx配置文件的简单结构

nginx的配置文件&#xff08;nginx.conf&#xff09;整体上可分为三个部分&#xff1a;全局块、events块、http块 区域职责全局块配置和nginx运行相关的全局配置events块配置和网络连接相关的配置http块配置代理、缓存、日志记录、虚拟主机等配置在http块中&#xff0c;可以包含…

python:傅里叶分析,傅里叶变换 FFT

使用python进行傅里叶分析&#xff0c;傅里叶变换 FFT 的一些关键概念的引入&#xff1a; 1.1.离散傅里叶变换&#xff08;DFT&#xff09; 离散傅里叶变换(discrete Fourier transform) 傅里叶分析方法是信号分析的最基本方法&#xff0c;傅里叶变换是傅里叶分析的核心&…

摆脱无用代码的负担:TreeShaking 的魔力

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

【教学类-06-12】20231126 (一)如何让加减乘除题目从小到大排序(以1-20之间加法为例,做正序排列用)

结果展示 优化后 优化前 背景需求&#xff1a; 生成列表 单独抽取显示题目排序方法 存在问题: 我希望 00 01 02……这样排序&#xff0c;但是实际上&#xff0c;除了第一个加数会从小到大排序&#xff0c;第二个被加数的第十位数和个位数都会从小到大排序&#xff0c;也就是…

NeoPreference延伸:为SharedPreferences配置项生成配置页面

代码地址&#xff1a;https://github.com/Nagi1225/NeoPreference.git 最初在开发NeoPreference这个SharedPreferences工具的时候&#xff0c;就期望完成三个目标&#xff1a; 代码简洁&#xff0c;新增配置项的时候一行代码&#xff08;最多两行&#xff09;&#xff1b;读写…

线程的常用方法-wait和notify以及线程的结束方式

再复习一下Java中的线程的状态图 wait和sleep的区别是&#xff1a;wait需要先持有锁&#xff08;wait需要再synchronized代码块中执行&#xff09;&#xff0c;执行后会让出锁。而sleep不需要先持有锁&#xff0c;执行后也不会释放锁&#xff08;有锁的话抱着锁睡觉&#xff09…

SpringBoot 环境使用 Redis + AOP + 自定义注解实现接口幂等性

目录 一、前言二、主流实现方案介绍2.1、前端按钮做加载状态限制&#xff08;必备&#xff09;2.2、客户端使用唯一标识符2.3、服务端通过检测请求参数进行幂等校验&#xff08;本文使用&#xff09; 三、代码实现3.1、POM3.2、application.yml3.3、Redis配置类3.4、自定义注解…

基于Haclon的标签旋转项目案例

项目要求&#xff1a; 图为HALCON附图“25interleaved_exposure_04”&#xff0c;里面为旋转的二维码标签&#xff0c;请将其旋转到水平位置。 项目知识&#xff1a; 在HALCON中进行图像平移和旋转通常有以下步骤&#xff1a; &#xff08;1&#xff09;通过hom_mat2d_ident…

jQuery_03 dom对象和jQuery对象的互相转换

dom对象和jQuery对象 dom对象 jQuery对象 在一个文件中同时存在两种对象 dom对象: 通过js中的document对象获取的对象 或者创建的对象 jQuery对象: 通过jQuery中的函数获取的对象。 为什么使用dom或jQuery对象呢&#xff1f; 目的是 要使用dom对象的函数或者属性 以及呢 要…

<JavaEE> 线程的五种创建方法 和 查看线程的两种方式

目录 一、线程的创建方法 1.1 继承 Thread -> 重写 run 方法 1.2 使用匿名内部类 -> 继承 Thread -> 重写 run 方法 1.3 实现 Runnable 接口 -> 重写 run 方法 1.4 使用匿名内部类 -> 实现 Runnable 接口 -> 重写 run 方法 1.5 使用 lambda 表达式 二…

Self Distillation 自蒸馏论文解读

paper&#xff1a;Be Your Own Teacher: Improve the Performance of Convolutional Neural Networks via Self Distillation official implementation&#xff1a; https://github.com/luanyunteng/pytorch-be-your-own-teacher 前言 知识蒸馏作为一种流行的压缩方法&#…

五种多目标优化算法(MOGWO、MOLPB、MOJS、NSGA3、MOPSO)求解微电网多目标优化调度(MATLAB代码)

一、多目标优化算法简介 &#xff08;1&#xff09;多目标灰狼优化算法MOGWO 多目标应用&#xff1a;基于多目标灰狼优化算法MOGWO求解微电网多目标优化调度&#xff08;MATLAB代码&#xff09;-CSDN博客 &#xff08;2&#xff09;多目标学习者行为优化算法MOLPB 多目标学习…

ps5ps4游戏室如何计时?计费系统怎么查看游戏时间以及收费如何管理

ps5ps4游戏室如何计时&#xff1f;计费系统怎么查看游戏时间以及收费如何管理 1、ps5ps4游戏室如何计时&#xff1f; 下图以佳易王计时计费软件V17.9为例说明 在开始计时的时候&#xff0c;只需点 开始计时按钮&#xff0c;那么开台时间和使用的时间长度项目显示在屏幕上&am…

如何判断一个题目用“贪心/动态规划“还是用“BFS/DFS”方法解决

1 总结 1.1 贪心、动态规划和BFS/DFS题解的关系 一般能使用贪心、动态规划解决一个问题时&#xff0c;使用BFS&#xff0c;DFS也能解决这个题&#xff0c;但是反之不能成立。 1.2 2 贪心 -> BFS/DFS 2.1 跳跃游戏1和3的异同 这两道题&#xff0c;“跳跃游戏”&#xf…

靡靡之音 天籁之声 ——Adobe Audition

上一期讲到了和Pr配合使用的字幕插件Arctime Pro的相关介绍。相信还记得的小伙伴应该记得我还提到过一个软件叫做Au。 当人们对字幕需求的逐渐满足&#xff0c;我们便开始追求更高层次的享受&#xff0c;当视觉享受在进步&#xff0c;听觉享受想必也不能被落下&#xff01; Au即…

Flutter桌面应用开发之毛玻璃效果

目录 效果实现方案依赖库支持平台实现步骤注意事项话题扩展 毛玻璃效果&#xff1a;毛玻璃效果是一种模糊化的视觉效果&#xff0c;常用于图像处理和界面设计中。它可以通过在图像或界面元素上应用高斯模糊来实现。使用毛玻璃效果可以增加图像或界面元素的柔和感&#xff0c;同…

一、深入简出串口(USRT)通信——基本概念。

一、前言 串口到底是什么&#xff1f;简单来说一句话就可以解释&#xff0c;串口就是一种通信协议。 看到这里可能大家会觉得你这不是放屁么&#xff0c;说了跟没说一样。所以这里做前言来描述&#xff0c;大家要先对通信协议有一个下意识地认识才能在学习串口的时候不至于迷茫…

spring循环依赖

Bean的生命周期 这里不会对Bean的生命周期进行详细的描述&#xff0c;只描述一下大概的过程。 Bean的生命周期指的就是&#xff1a;在Spring中&#xff0c;Bean是如何生成的&#xff1f; 被Spring管理的对象叫做Bean。Bean的生成步骤如下&#xff1a; Spring扫描class得到Bean…
最新文章