你真的熟悉多线程的程序的编写?快来查漏补缺

news/2024/10/17 16:25:31/

目录

一、Thread 类的属性及常用的构造方法

1.1、 Thread 常见构造方法

1.2、Thread  类的常见属性

1.3、启动(创建)一个线程

1.4、中断一个线程

1.5、等待一个线程

1.6、休眠当前线程

1.7、当前线程让出的 CPU 资源

二、线程状态


一、Thread 类的属性及常用的构造方法


1.1、 Thread 常见构造方法

方法说明
Thread()创建线程对象
Thread(Runnable target)使用 Runnable 对象创建线程对象
Thread(String name)

创建线程对象,并命名(名字是可以重复的);

Thread(Runnable target, String name)使用 Runnable 对象创建线程对象,并命名
【了解】Thread(ThreadGroup group, Runnable target)线程可以被用来分组管理,分好的组即为线程组,这 个目前我们了解即可

Ps:创建线程的时候,给线程起个名字还是很有必要的,目的就是为了方便程序员调试,一旦出问题,方便找到对应的代码,如果不手动起名,JVM 默认会起名为 thread-0、thread-1......

1.2、Thread  类的常见属性

获取方法解释
getId()线程的 id,也是线程的唯一标识,不同线程不会重复
getName()名称,就是构造方法里给参数起的名字
getState()线程的状态(后面会细说)
getPriority()优先级,优先级高的线程理论上更容易被调度到
isDaemon()是否是“后台线程”,这里需要记住一点——如果是“前台线程”,那么当 main 运行完了,前台线程还没完,进程是不会退出的!如果是后台线程,那么当 main 等其他前台线程运行完了,即使后台线程没执行完,进程也会退出!
isAlive()判定内核线程还在不在,可以简单 理解为 run 方法执行完了,内核线程就销毁了。
isInterrupted()线程是否被中断
currentThread()返回当前线程对象的引用

1.3、启动(创建)一个线程

之前我们通过 new Thread 只是创建出了 Thread 对象,并没真正创建除线程!!!

Thread 对象虽然和内核中的线程是一一对应的,但是生命周期并非完全相同:Thread 对象创建出来了,内核中的线程还不一定有,调用 start 方法,内核的线程才创建出来当 run 运行完了,内核中的线程就销毁了,但是 Thread 对象还在。

Ps:直接调用 run 并不会创建线程,只是运行线程中的代码,调用 start 方法,才是创建了线程

1.4、中断一个线程

当 run 方法执行完了,线程就销毁了,那有没有办法让线程提前结束呢?

使用 thread 的 interrupt 方法就通知这个线程进行中断,这个线程具体如何处理,还需要看 run 里的逻辑是如何实现的,主要有以下几种情况:

  • thread 线程在运行状态,那么就设置标志 isInterrupted() 标志位为 true。 
  • thread 线程在阻塞状态(sleep),不会设置标志位,而是触发一个 InterruptedException  异常,这个异常会把 sleep 提前唤醒。

使用 interrupt 本质是让 run 方法尽快结束,而不是 run 执行一半强制结束!!!

最后,我们可以通过以下两种方法来看线程中断的标志位是否设置

标志位是否清除, 就类似于一个开关.

Thread.isInterrupted() 判断当前线程的中断标志是否被设置,清除中断标志(相当于按下开关, 开关自动弹起来了. 这个称为 "清除标志位")

Thread.currentThread().isInterrupted() 判断指定线程的中断标志是否被设置,不清除中断标志 (相当于按下开关之后, 开关弹不起来, 这个称为 "不清除标志位".)

a)例如使用 Thread.isInterrupted() 判断线程是否收到中断通知后,标志位会被清除. 如下:

    public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(new Runnable() {@Overridepublic void run() {for(int i = 0; i < 5; i++) {System.out.println("当前线程是否收到中断的通知:" + Thread.interrupted());}}});thread.start();thread.interrupt();}

执行结果如下:

b) 使用 Thread.currentThread().isInterrupted() 判断线程是否收到中断通知后,标记位不会清除:

    public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(new Runnable() {@Overridepublic void run() {for(int i = 0; i < 5; i++) {System.out.println("当前线程是否收到中断的通知:" + Thread.currentThread().isInterrupted());}}});thread.start();thread.interrupt();}

1.5、等待一个线程

线程之间的调度顺序是完全随机的,但我们也可以通过一些特殊的操作来对线程的执行顺序进行干预,其中 join 就是一个办法~

在 main 方法中调用 t1.join 的效果就是让main 线程阻塞等待,等到 t1 线程执行完了,main线程才继续执行,如下代码:

    public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {for(int i = 0; i < 5; i++) {System.out.println("正在运行 t1 线程");}}});t1.start();t1.join();for(int i = 0; i < 5; i++) {System.out.println("正在运行 main 线程");}}

执行效果:

如果去掉 t1.join(),效果如下:

另外,join 还有一个带等待时间的版本,如下:

 也就是当超过了这个等待时间,就过时不候~

实际的开发中大多数都是指定了最大等待时间,避免程序“卡死”的情况~

1.6、休眠当前线程

sleep 指定休眠时间,可以让线程休息(阻塞)一会;

Thread 类下的静态方法说明
public static void sleep(long millis) throws InterruptedException

休眠当前线程 millis

毫秒

public static void sleep(long millis, int nanos) throws InterruptedException可以更高精度的休眠

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

底层原理如下:

操作系统管理这些线程的 PCB 的时候,是有多个链表的,调用了sleep ,就会把 PCB 移动另外一个“阻塞队列”(原先在“就绪队列”),当 sleep 时间到了 ,就会被移动到之前的就绪队列。

Ps:移回了就绪队列,不代表立即就能在 CPU 上执行,还得看系统什么时候调用这个线程~

1.7、当前线程让出的 CPU 资源

通过 Thread.yield() 方法可以暂停当前正在执行的线程(让出当前的 CPU  资源),并执行其他线程。yield 做的就是让当前运行线程回到可运行的状态(就绪状态),以具有同样的优先级获得运行机会,也因此,实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。

Ps:yield()从未导致线程转到等待/睡眠/阻塞状态。在大多数情况下,yield()将导致线程从运行状态转到可运行状态,但有可能没有效果。

二、线程状态


线程的状态是一个枚举类型 Thread.State。

不要被这个图吓到,他简化以后是这样的:

我们重点在于理解各个状态的意思:

  • NEW: Thread 对象创建出来了,但是内核中的线程还没有创建。
  • TERMINATED: 内核中的线程销毁了(线程的 run 方法执行完了),但是 Thread 对象还在。
  • RUNNABLE: 就绪状态(正在 CPU 上运行 + 在就绪队列中排队)。
  • TIMED_WAITING: 按照一定时间,进行阻塞(sleep)。
  • WAITING: 特殊的阻塞状态,调用 wait。
  • BLOCKED: 等待锁的时候进入阻塞状态。


http://www.ppmy.cn/news/59387.html

相关文章

Android 内存分析(java/native heap内存、虚拟内存、处理器内存 )

1.jvm 堆内存(dalvik 堆内存) 不同手机中app进程的 jvm 堆内存是不同的&#xff0c;因厂商在出厂设备时会自定义设置其峰值。比如,在Android Studio 创建模拟器时&#xff0c;会设置jvm heap 默认384m , 如下图所示&#xff1a; 当app 进程中java 层 new 对象(加起来总和)占用…

只使用位运算实现加减乘除

在线OJ: LeetCode 29. 两数相除 原题目的要求是不能使用乘法, 除法和取余运算符实现除法. 在本篇博客中把题目要求提高一点, 这里只使用位运算来实现, 顺便的也就把只使用位运算实现加减乘除实现了. 1 . 实现加法 首先我们需要知道两数之和可以是两个数位相加和不进位相加之…

HTTP代理出现503错误是什么意思,要如何修复?

在使用HTTP代理的时候&#xff0c;我们常常会遇到各种问题&#xff0c;想要解决&#xff0c;就需要根据返回码来解决。今天我们来说说&#xff0c;遇到HTTP 代理出现 503 服务不可用错误要怎么办&#xff0c;该如何解决呢&#xff1f; 首先&#xff0c;我们要明白&#xff0c;…

成为数据分析师,需要具备哪些技能?

随着互联网的发展&#xff0c;数据分析师的特点越来越明显&#xff0c;对数据分析师综合素质的要求也较高。 1、较强的数据挖掘、信息整理、和逻辑分析能力 数据分析&#xff0c;也是数据分析师的一个方向。 制作日常性的经营报表&#xff0c;对公司或者行业KPI指标进行拆解…

Java 中的线程是什么,如何创建和管理线程-上(十一)

Java 中的线程是指程序中可以独立执行的最小单位。在 Java 中&#xff0c;创建线程通常有两种方式&#xff1a;继承 Thread 类和实现 Runnable 接口。线程的管理包括控制线程状态、线程优先级、线程同步等。 一、Java 中的线程 线程是程序中能够独立执行的最小单位&#xff0…

JavaWeb05(删除增加修改功能实现连接数据库)

目录 一.实现删除功能 1.1 url如何传参&#xff1f; xx.do?参数参数值&参数名参数值 1.2 servlet如何拿对应值&#xff1f; //根据参数名拿到对应的参数值 String str req.getParameter("参数名") 1.3 如何询问&#xff1f; οnclick"return con…

20230501-win10-制作U盘启动盘-firpe

20230501-win10-制作U盘启动盘-firpe 一、软件环境 zh-cn_windows_10_consumer_editions_version_22h2_updated_march_2023_x64_dvd_1e27e10b.isofirpe 1.8.2标签&#xff1a;firpe win10分栏&#xff1a;WINDOWS 二、硬件环境 8G或以上的U盘一个FX86笔记本一台 三、官方下…

自动驾驶中地图匹配定位技术总结

引言 汽车定位是让自动驾驶汽车知道自身确切位置的技术&#xff0c;在自动驾驶系统中担负着相当重要的职责。汽车定位涉及多种传感器类型和相关技术&#xff0c;主要可分为卫星定位、惯性导航定位、地图匹配定位以及多传感器融合定位几大类。其中地图匹配定位技术利用道路物理…