当前位置: 首页 > news >正文

多线程Thread常用方法和状态

Thread类 及常见方法

1、常见构造方法

方法说明
Thread()创建线程对象
Thread(Runnable target)使用 Runnable 对象创建线程对象
Thread(String name)创建线程对象,并命名
Thread(Runnable target, String name)使用 Runnable 对象创建线程对象,并命名
Thread(ThreadGroup group,线程可以被用来分组管理,分好的组即为线程组(了解即可,不详细讲)

上篇文章基本上都讲解过了,我们这里只讲解两个Thread(String name)以及Thread(Runnable target, String name) 来讲解

这两个方法都可以在创建线程的时候,给线程起给名字(这个名字运行重复),目的就是为了方便程序员调试。一旦出问题,就更方便找到对应的代码。

如果不自定义名字,JVM 就会按照thread-0 ,thread-1…来命名

1.1Thread(Runnable target, String name)

public class Main {public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(() -> {while (true) {try {Thread.sleep(3000);System.out.println("hello");} catch (InterruptedException e) {throw new RuntimeException(e);}}}, "Thread线程自定义名字");thread.start();}}

通过jconsole来查看验证:

image-20230221223255358

2、Thread 的几个常见属性

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

ID:

Java中给Thread对象安排的身份标识;和操作系统内核中的PCB的pid以及和操作系统提供的线程线程API中的线程id都不是一回事。

名称:

就是前面构造方法中我们自己自定义的名称

状态:

下面单独讲。

优先级:

获取线程的优先级

后台线程:

默认创建的是前台线程,后台线程指的是当所有(main线程)前台线程执行完毕以后,不管后台线程有没有执行完,直接退出进程。前台进程就不会。

是否存活:

判定内核的线程在不在!

Thread对象虽然和内核中的线程是一一对应的,但是生命周期并非完全相同。

Thread t = new Thread();这样虽然Thread对象出来了,内核里的线程还不一定有;

调用start方法,内核线程才会有,当内核里的线程执行完了(run方法运行完了),内核的线程就销毁了,但是Thread对象还在。

[经典的面试题]

调用 start 才会真正创建线程!! 不调用 start 没创建线程 (在内核里创建 PCB)
注意理解, start 和 run 的区别!! **
直接调用 run 并没有创建线程,只是在原来的线程中运行的代码
调用 start,则是
创建了线程**,在新线程中执行代码(和原来的线程是并发的)

线程的中断:

run方法执行完了,线程就结束了.有没有办法,让线程,提前一点结束呢??

通过线程中断的方式来进行;

本质:让run方法尽快结束,而不是run执行一半强制结束。

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

  1. 通过共享的标记来进行沟通

  2. 调用 interrupt() 方法来通知

1、通过共享的标记来进行沟通:

image-20230222004144060

2、使用标准库里面自带的一个标志位

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

方法说明
public static boolean interrupted()判断当前线程的中断标志位是否设置,调用后清除标志位
public boolean isInterrupted()判断对象关联的线程的标志位是否设置,调用后不清除标志位

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

在主线程,通过thread.interrupt()设置标志位为true!

2.1使用Thread.currentThread().isInterrupted()判断当前线程的中断标志被设置

image-20230222012016003

启动时我们发现:

image-20230222012055034

报错了,并且线程也没有中断,为什么?

2.1.1、interrupt方法的行为,有两种情况:

1、t线程在运行状态:

调用t.interrupt()会设置标志位为true。结果**!**后正常中断。

image-20230222012331417

2、线程在阻塞状态( wait/join/sleep 等方法而阻塞挂起)

不会设置标志位,而是触发一个InterruptedException 异常的形式通

知,清除中断标志,并且提前将阻塞状态唤醒!

前面有讲到线程中断的本质是:本质:让run方法尽快结束,而不是run执行一半强制结束。

下面就可以体现出来,当遇到InterruptedException 异常的形式通知时候。会触发这个异常:

image-20230222012644466

具体的如何处理,就在于补抓到异常以后用户的代码是如何写

①立即结束进程:

image-20230222012818394

②不理会:

QQ录屏20230222013027 00_00_00-00_00_30

③稍后处理:

image-20230222013253379

2.2使用Thread.interrupted() 判断当前线程的中断标志被设置

image-20230222013842513

当这个使用这个作为标志位时候调用thread.interrupt()时候问题同上;

他们两个方法的不同之处就在于:

image-20230222014008962

什么叫做清楚标志位,什么叫做不清除?

清除标志位:

public class ThreadDemo {public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(()-> {for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().interrupted());}});thread.start();thread.interrupt();}
}

打印结果:

true // 只有一开始是 true,后边都是 false,因为标志位被清

false

false

false

false

false

false

false

false

false

不清除标志位:

public class ThreadDemo {public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(()-> {for (int i = 0; i < 10; i++) {System.out.println(Thread.interrupted());}});thread.start();thread.interrupt();}
}

true // 全部是 true,因为标志位没有被清

true

true

true

true

true

true

true

true

true

3、等待一个线程-join()

join()可以控制线程之间的结束顺序

线程之间的调度顺序,是不确定的!!可以通过一些特殊操作,来对线程的执行顺序,做出干预其中 join 就是一个办法,控制线程之间的结束顺序!!

Tips:Java 中的多线程方法,只要是这个方法会阻塞,都可能会抛出InterrupttedException 异常!!

效果:在main中调用t.join效果就是让main线程阻塞等待,等到t执行完了,main才继续执行!!

public class ThreadDemo {public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(()-> {for(int i = 0; i < 10; i++) {}});t.start();t.join();}
}

当调用t.join()时候,main线程进入阻塞,不参与CPU的调度。

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

实际应用:

服务器开发经常要处理客户端的请求,根据请求计算生成响应 就需要用到“等待时间”称为“超时时间“客户端发了请求过来,等待响应,这个等待就不能无限的等 可以根据需要,约定 1000ms 500ms如果时间之内响应没有回来,客户端直接提示“等待超时

4、获取当前线程引用

public static Thread currentThread() :返回当前线程对象的引用

5、休眠当前线程

注意:由于线程的调度是不可控制的,所以,这个方法只能保证实际休眠时间是大于等于参数设置的休眠时间的

方法说明
public static void sleep(long millis) throws InterruptedException休眠当前线程 millis毫秒
public static void sleep(long millis, int nanos) throws可以更高精度的休眠

sleep:指定休眠的之间(阻塞一会会)

如何更加好的理解sleep?

操作系统管理这么多个PCB的时候是有多个链表的,调用了sleep,则当前调用sleep的PCB就会被移动到另外的“阻塞队列”中。如图:

image-20230222084250484

image-20230222084341685

通过上诉案例我们也能够清楚的认识到,使用sleep(1000)的话,实际阻塞时间是大于等于1s的,这完全取决于调度了。

状态-Thread常见属性

这里特指Java中的对Thread的状态规则

NEW:Thread对象创建出来了,但是内核的PCB还没创建。

TERMINATED:内核的PCB销毁了,但是Thread对象还在。

RUNNABLE:就绪状态(正在CPU上运行 + 在就绪队列中排队)

TIMED_WAITING:按照一定的时间,进行阻塞。

WAITING:特殊的阻塞状态(调用wait)

BLOCKED:等待锁的时候竟然的阻塞状态

一个线程状态之间的变化如下图:

image-20230222184010978

http://www.lryc.cn/news/16876.html

相关文章:

  • Codeforces Round #836 (Div. 2)
  • Python学习之项目实践: 写一个MP3播放器
  • RocketMQTemplate 实现消息发送
  • 教师干货丨这5款微课必备提效神器,我要告诉全世界!
  • timm使用swin-transformer
  • 【java基础】java八大基本数据类型和运算符
  • Mybatis源码学习笔记(四)之Mybatis执行增删改查方法的流程解析
  • 浅谈测试用例设计
  • python 利用装饰器实现类似于flask路由
  • git 拉取远程分支到本地
  • Answering Multi-Dimensional Range Queries under Local Differential Privacy
  • 手把手搭建springboot项目05-springboot整合Redis及其业务场景
  • Flutter基础语法(六)var、final、const、late
  • Linux之安装node
  • 二叉树、二叉搜索树、二叉树的最近祖先、二叉树的层序遍历【零神基础精讲】
  • 【算法】【数组与矩阵模块】求最长可整合子数组和子数组的长度
  • 数据结构:循环队列的实现(leetcode622.设计循环队列)
  • [qiankun]实战问题汇总
  • Kafka(6):服务端常用参数配置
  • 2023爱分析·云原生智能运维中台市场厂商评估报告:秒云(miaoyun.io)
  • hadoop容器化部署
  • 【07-JVM面试专题-JVM运行时数据区的虚拟机栈你知道吗?它的基本结构是什么呢?你知道栈帧的结构吗?那你说说动态链接吧?】
  • Java性能优化-GC优化基础
  • 【Tomcat】IDEA编译Tomcat源码-手把手教程
  • 如何弄小程序?公司企业可以这样做小程序
  • 【Git】IDEA集合Git和码云
  • [USACO03FALL / HAOI2006] 受欢迎的牛 G(C++,强连通分量)
  • Vue 动态路由接口数据结构化为符合VueRouter的声明结构及菜单导航结构、动态路由懒加载方法
  • Python----------字符串
  • 日志收集笔记(架构设计、Log4j2项目初始化、Lombok)