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

news2024/11/28 3:51:06

目录

一、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.coloradmin.cn/o/1254457.html

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

相关文章

基于Haclon的图形镜像案例

项目要求&#xff1a; 图为HALCON的例图“green-dot”&#xff0c;请将其中的圆形图案按水平和垂直两个方向分别进行镜像。 项目知识&#xff1a; 首先要用BLOB分析的方法&#xff0c;得到圆形图案的目标区域&#xff0c;再对其进行镜像。 在HALCON中与镜像相关的算子为mirr…

跟着chatgpt学习|1.spark入门

首先先让chatgpt帮我规划学习路径&#xff0c;使用Markdown格式返回&#xff0c;并转成思维导图的形式 目录 目录 1. 了解spark 1.1 Spark的概念 1.2 Spark的架构 1.3 Spark的基本功能 2.spark中的数据抽象和操作方式 2.1.RDD&#xff08;弹性分布式数据集&#xff09; 2…

实战oj题——括号匹配问题

前言&#xff1a;前面我们已经做了一些关于顺序表和链表的oj题&#xff0c;今天我们就来解决一些有关于栈和队列的oj题。 我们对这个题看起来毫无头绪&#xff0c;但是我们刚学习了栈&#xff0c;就可以用栈来解决这一类问题&#xff0c;如果我们读取到左括号就入栈&#xff0c…

Cache学习(3):Cache地址映射(直接映射缓存组相连缓存全相连缓存)

1 Cache的与存储地址的映射 以一个Cache Size 为 128 Bytes 并且Cache Line是 16 Bytes的Cache为例。首先把这个Cache想象成一个数组&#xff0c;数组总共8个元素&#xff0c;每个元素大小是 16 Bytes&#xff0c;如下图&#xff1a; 现在考虑一个问题&#xff0c;CPU从0x0654…

再见 Pandas,再见算法

大家好,《再见pandas》 系列已有200多位朋友加入学习了,这段时间亲眼见证了很多朋友的飞跃进步,从无到有,从一个问问题的小白到开始慢慢回答别人的问题,在讨论和练习中不断成长。虽说pandas已经很普及了,但普及内容的深度却远远不够。 下面这套原创图文是我和几位小伙伴…

深入了解 Pinia:现代 Vue 应用的状态管理利器

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

手把手教你如何在Linux下写进度条小程序(附源码)

效果展示 录屏2023 一、建立文件 mkdir ProgressBar //在当前目录下&#xff0c;建立新的目录 cd ProgressBar //进入这个目录 touch main.c makefile progressbar.c progressbar.h //在ProgressBar这个目录建立这几个文件进入ProgressBar这个目录之后&#xff0c;使…

python树长子兄弟链存储结构(孩子兄弟链存储结构)

长子兄弟链存储结构&#xff08;孩子兄弟链存储结构&#xff09;解释&#xff1a; 长子兄弟链存储结构是一种树的存储结构&#xff0c;它使用孩子兄弟表示法&#xff08;也称作左孩子右兄弟表示法&#xff09;来表示树的结构。这种表示方法主要用于存储一般的树&#xff0c;而不…

Linux服务器SSH客户端断开后保持程序继续运行的方法

目录 1. nohup 命令&#xff1a; 2. tmux 或 screen&#xff1a; 3 final shell 断开后服务器如何继续执行令&#xff1f; 方法一&#xff1a;使用 nohup 命令 方法二&#xff1a;将命令放在后台执行 4 你可以使用 jobs 命令查看当前终端中正在后台运行的任务 &#xff…

飞翔的鸟小游戏

第一步是创建项目 项目名自拟 第二步创建个包名 来规范class 再创建一个包 来存储照片 如下&#xff1a; package game; import java.awt.*; import javax.swing.*; import javax.imageio.ImageIO;public class Bird {Image image;int x,y;int width,height;int size;doub…

Qt4用子类化ProxyModel和子类化MainWindow实现全表筛选,中文排序和复制粘贴

目录 1 需求 2 子类化ProxyModel实现全表筛选 3 字符串列表实现中文排序 3.1 Qt5中文排序 3.2 Qt4排序 4 表格的复制粘贴 5 应用 1 需求 模型视图编程是Qt开发的基本功&#xff0c;其中有几个关键问题需要解决&#xff1a; 全表筛选&#xff0c;或者说多列搜索中文排序…

【精选必看】MyBatis映射文件及动态SQL,一级,二级缓存介绍

文章目录 MyBatis映射文件 < r e s u l t M a p > <resultMap> <resultMap>resultMap < sql>&< include>特殊字符处理 动态SQL < i f > < if> <if> < w h e r e > <where> <where> < s e t > <…

叠加原理(superposition principle)、线性系统

叠加原理&#xff08;superposition principle&#xff09;&#xff1a;指对一个系统而言&#xff0c;两个或多个输入产生的输出&#xff0c;等于这几个输入单独引起的输出的和&#xff0c;即输入的叠加等于各输入单独引起的输出的叠加。 线性系统&#xff1a;一个系统&#x…

SAP Smartform小结

SAP系统做打印单据用的, 感觉很不好用, 特别是要嵌入韩文时必须使用嵌入的word编辑器,运行速度简直不可忍受. 见过一些Adobe interactive form的示例, 看着相当不错, 不过据说需要花money额外买licence, 哪有smartform这种免费东西来得实惠. 一般打印需求,会要求有标题抬头,打…

【开源】基于Vue+SpringBoot的农家乐订餐系统

项目编号&#xff1a; S 043 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S043&#xff0c;文末获取源码。} 项目编号&#xff1a;S043&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 用户2.2 管理员 三、系统展示四、核…

CSDN助手:一键下载CSDN博客:高效保存,随时阅读

文章目录 &#x1f4d6; 介绍 &#x1f4d6;&#x1f3e1; 环境 &#x1f3e1;&#x1f4d2; 使用方法 &#x1f4d2;⚓️ 相关链接 ⚓️ &#x1f4d6; 介绍 &#x1f4d6; 这是我自己无聊的时候写的一个应用&#xff0c;以前UI有点丑&#xff0c;这次重写了一下UI 功能如下 …

球面的表面积

此推导需要用到重积分的知识&#xff0c;另外关于曲面的面积公式可以看我之前的博客

过滤器的应用

javaWeb三剑客: 1. Servlet:接收请求,处理请求(单例,也就是说&#xff0c;多个用户请求的的servlet是同一个对象) 2. Filter:拦截请求(单例->也就是说&#xff0c;多个用户请求的的filter是同一个对象) 3. Listem: 监听用户/服务器行为,javaWeb三剑客: 过滤器的实现 1&…

24. 深度学习进阶 - 矩阵运算的维度和激活函数

Hi&#xff0c;你好。我是茶桁。 咱们经过前一轮的学习&#xff0c;已经完成了一个小型的神经网络框架。但是这也只是个开始而已&#xff0c;在之后的课程中&#xff0c;针对深度学习我们需要进阶学习。 我们要学到超参数&#xff0c;优化器&#xff0c;卷积神经网络等等。看…

用xlwings新建一个excel并同时生成多个sheet

新建一个excel并同时生成多个sheet&#xff0c;要实现如下效果&#xff1a; 一般要使用数据透视表来快速实现。 今天记录用xlwings新建一个excel并同时生成多个sheet。 import xlwings as xw # 打开excel,参数visible表示处理过程是否可视,add_book表示是否打开新的Excel程序…