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

Java多线程(二)

目录

一. Thread类及常见方法

1.1 Thread类的常见构造方法

1.2 Thread 的几个常见属性

1.3 启动一个线程

1.4 中断一个线程

1.4.1 通过共享的标记来进行沟通

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

1.5 等待一个线程

1.6 线程状态


一. Thread类及常见方法

每一个线程都有一个唯一的Thread对象与之关联。

1.1 Thread类的常见构造方法

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

下面是例子:

        Thread t=new Thread();Thread b=new Thread(new MyRunnable());Thread n=new Thread("t1");Thread l=new Thread(new MyRunnable(),"t2");

1.2 Thread 的几个常见属性

注意:

  • ID是线程的身份标识,每一个线程都有唯一的的ID
  • 名称是在调试工具中才会用到的
  • 后台线程:不会阻止整个就进程结束(后台线程的结束不会影响到其他线程的结束)
  • 前台线程:阻止整个就进程结束(一个进程中前台线程可以不止有一个,只有当所有的前台线程都结束后,整个进程才会结束
  • main线程与代码中手动创建的线程被默认为前台线程
  • 设置后台(守护)线程必须要在start之前将该线程设置为后端线程(使用 setDaemon()

下面是setDaemon()方法的定义:

在Java中, setDaemon 是 Thread 类中的一个实例方法,用于将指定的线程设置为守护线程。以下是关于它的详细定义及相关信息:
 
方法签名
 
java   
public final void setDaemon(boolean on)
 
 
参数说明
 
 on :是一个布尔类型的参数。当 on 的值为 true 时 ,表示将调用该方法的线程设置为后台线程(守护线程;当 on 的值为 false 时,表示将其设置为前台线程(非守护线程)。

1.3 启动一个线程

使用 start()

我们只要记住:调用start方法,才真的在操作系统的底层创建出一个线程.

class MyRunnable implements Runnable{@Overridepublic void run() {System.out.println("hi");}
}
public class text {public static void main(String[] args) {Thread b=new Thread(new MyRunnable());b.start();}
}

注意:一个线程对象只能调用一次start()(线程的生存周期具有不可逆性)

1.4 中断一个线程

1.4.1 通过共享的标记来进行沟通

import java.util.Scanner;public class text {public volatile static boolean mm=true;public static Thread t=new Thread(()->{while(mm){System.out.println("hi");try {Thread.sleep(10000);} catch (InterruptedException e) {throw new RuntimeException(e);}}System.out.println("线程已中断");});public static void main(String[] args) throws InterruptedException {t.start();Thread.sleep(10000);System.out.println("是否中断,如要中断请输入0");Scanner sc=new Scanner(System.in);int n= sc.nextInt();if(n==0){mm=false;}}
}

上述代码的mm变量就是一个共享变量,通过该标记就可以实现类似与对线程的中断

注意:在使用该方法进行线程的中断时,共享标记要加上volatilevolatile可以解决内存的可见性问题

内存可见性问题的定义:当一个线程修改一个共享变量的值时,这个值会被保存在该线程的本地内存中,而不是直接写入主内存中。如果其他线程需要读取该共享变量的值,它们可能会从自己的本地内存中读取旧值,而不是从主内存中读取最新的值,从而导致数据不一致的问题。这时volatile就可以帮助我们解决这个问题

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

下面是中断线程的一种方式,但是在我们在日常开发中一般不会使用这种方法,在后面会提出中断线程更科学的方法

import java.util.Scanner;public class text {public static void main(String[] args) {Thread t=new Thread(()->{Thread m=Thread.currentThread();while(!m.isInterrupted()){System.out.println("hi");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});t.start();System.out.println("是否中断线程,中断请输入0");Scanner sc=new Scanner(System.in);int n=sc.nextInt();if(n==0){t.interrupt();}}
}

下面是中断线程要用到的方法

注意:

  • Thread.currentThread()是一个静态方法,它能返回一个引用,这个引用指向当前正在执行的线程
  • Thread m=Thread.currentThread();
                while(!m.isInterrupted())
    这两行代码可以改为while(Thread.currentThread().isInterrupted()),效果是一样的
  • 中断标志位:在Thread类中一个boolean状态的属性,初始是false(false:表示线程未被请求中断  true:表示线程已被请求中断 )

  • interrupt():发出请求线程中断的指令实际在代码逻辑中并不是interrupt直接中断的线程,而是间接的),将中断标志位设置为true如果sleep正在执行时(只要是sleep/join/wait等让线程阻塞的方法都会),interrupt()会“唤醒”sleep提前结束sleep导致的阻塞状态),并且触发并抛出异常,从而中断线程,并且这时sleep会自动把当前线程中断的标志位重新设置为false
  • interrupted():只是检查当前线程的中断标志位,并清除当前中断标志位即 将 true 变回false),不会主动唤醒处于sleep状态的线程
  • isInterrupted():只是判断当前线程的中断标志位(只做判断,不会修改当前线程的中断标志位)。如果中断标志位是true则返回true,反之则返回false

上述代码中还有一个隐藏的问题:lambada表达式中我们在引用当前线程时,为什么不直接用 t 呢,而是用Thread.currentThread()获取当前线程的引用呢?

答:在使用lambada表达式构建Thread对象时,是先执行lambada表达式中的内容,再将其作为Thread类构造方法的参数,所以在lambada表达式中t这个概念并不存在。而Thread.currentThread()获取的引用就是lambada表达式所在线程的引用(与t所指向的线程是一致的)。


下面我们来看一个类似的的代码

import java.util.Scanner;public class text {public static void main(String[] args) {Thread t=new Thread(()->{Thread m=Thread.currentThread();while(!m.isInterrupted()){System.out.println("hi");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}});t.start();System.out.println("是否中断线程,中断请输入0");Scanner sc=new Scanner(System.in);int n=sc.nextInt();if(n==0){t.interrupt();}}
}

我把 throw new RuntimeException(e)删掉,加上 e.printStackTrace();这样有什么不一样的效果呢?

在调用t.interrupt()时,中断标志位被设置成true,并且唤醒sleep,并引发异常,而e.printStackTrace()只是打印异常而不是抛出异常,所以线程不会被中断。然后sleep将中断标志位重新设置为false,while的判断条件就会变为true,导致while循环一直被循环执行,不会中断线程。执行结果就是:在输入0后,打印一遍异常,在这后一直循环打印hi(注意:一次interrupt()调用,对于正在sleep的线程只会唤醒一次sleep,当循环下一次执行到sleep时,sleep只会引起线程阻塞,而不会引发异常

如果想要立即退出,我们可以才用以下改变(使用break退处即可)

import java.util.Scanner;public class text {public static void main(String[] args) {Thread t=new Thread(()->{Thread m=Thread.currentThread();while(!m.isInterrupted()){System.out.println("hi");try {Thread.sleep(1000);} catch (InterruptedException e) {break;}}});t.start();System.out.println("是否中断线程,中断请输入0");Scanner sc=new Scanner(System.in);int n=sc.nextInt();if(n==0){t.interrupt();}}
}

1.5 等待一个线程

等待线程:干预两个线程的结束顺序,后结束的线程等待先执行的线程执行完才开始执行

用到的方法:join()

下面代码执行的功能:先让线程t执行(count++自增)完毕,再让主线程打印出自增结束最后得到的count的值

public class Test {public static int count=0;public static void main(String[] args) throws InterruptedException {Thread t=new Thread(()->{for(int i=0;i<5000;i++){count++;}});t.start();t.join();System.out.println("count自增后的值为: "+count);}
}

注意:

  • 由于join()会让线程陷入阻塞状态,因此要处理异常(与sleep类似)
  • 在主线程中0调用t.join()是让主线程(main线程)等待t线程执行完后再执行
  • 一开始执行的是主线程,然后执行t.start()方法后就变成了多线程

在上述代码中如果让join()与print语句调换顺序会得到不一样的效果,这是为什么呢

代码里  join()  和  print  的“书写顺序”,会决定主线程“有机会执行时”的优先选择。比如:
 
- 若代码是“ t.start(); print; join; ”:主线程竞争到 CPU 时,会先执行  print ,再执行  join ;

- 若代码是“ t.start(); join; print; ”:主线程竞争到 CPU 时,会先执行  join (然后阻塞等子线程),再执行  print 。
 
简单说:CPU 调度决定“主线程何时能执行”,而代码顺序决定“主线程能执行时,先做哪件事”。

上述的代码是主线程等待t线程完成功能的,其实也可以让t线程等待主线程完成功能,下面是具体代码:

public class text {public static int count=0;public static void main(String[] args) throws InterruptedException {Thread mainThread=Thread.currentThread();//首先获得主线程的引用Thread t=new Thread(()-> {//然后创建并启用t线程try {mainThread.join();} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(count);});t.start();for (int i = 0; i < 5000; i++) {count++;}}
}

注意:

  • join的调用位置:如果是t线程要等待m线程,则在t线程中调用m线程的join实例方法
  • Thread.currentThread()是获取当前所在线程的引用,要注意其使用的位置

补充:

join()等待线程结束(死等)
join(log millis)等待线程结束,最多等 millis 毫秒 
join(log millis ,int nanos)同理,但可以更高精度

sleep(0)--特殊情况:主动放弃当前线程的剩余时间片(固定时长的CPU使用时间),让CPU优先调度其他就绪状态的线程--->降低CPU的使用率

1.6 线程状态

  •  NEW : 线程对象已建立(new  Thread()),但尚未调用start()方法
  • TERMINATED :操作系统中线程已经销毁,但是Thread对象还在(线程入口方法执行完毕)
  • RUNNABLE : 线程即将执行(线程在就绪队列中)或者正在执行
  • BLOCKED :特指由于锁引起的阻塞
  • WAITING : 死等进入的阻塞状态(如 join())
  • TIMED_WAITING :带有超时时间的阻塞等待


文章转载自:

http://GbMEWRvB.gjmLL.cn
http://tdltiT3n.gjmLL.cn
http://3v3AmTUB.gjmLL.cn
http://NL3Kiqjx.gjmLL.cn
http://cFJ97JwZ.gjmLL.cn
http://2hos0HeM.gjmLL.cn
http://NKDpmCyD.gjmLL.cn
http://poibOZDU.gjmLL.cn
http://hS9ETNM0.gjmLL.cn
http://hTYMUlBy.gjmLL.cn
http://5uazWe0c.gjmLL.cn
http://35TXUV65.gjmLL.cn
http://OnaWFPeD.gjmLL.cn
http://eAvTn9Wk.gjmLL.cn
http://G6ZbR18o.gjmLL.cn
http://ciFIvSIP.gjmLL.cn
http://lzf68RHn.gjmLL.cn
http://cqr8JLJy.gjmLL.cn
http://trwvyF4L.gjmLL.cn
http://0FHAmiDZ.gjmLL.cn
http://KunWLNoW.gjmLL.cn
http://90kNQ7eL.gjmLL.cn
http://IcdpIyIn.gjmLL.cn
http://TFyPEHXN.gjmLL.cn
http://bvinCt4K.gjmLL.cn
http://C3w2RyqB.gjmLL.cn
http://VipPxrIk.gjmLL.cn
http://SYvPvgYm.gjmLL.cn
http://e4KJs4fV.gjmLL.cn
http://vfXsiOGy.gjmLL.cn
http://www.dtcms.com/a/381623.html

相关文章:

  • STM32 单片机开发 - SPI 总线
  • 【笔记】Windows 安装 TensorRT 10.13.3.9(适配 CUDA 13.0,附跨版本 CUDA 调用维护方案)
  • 基于PHP的鲜花网站设计与实现
  • 如果系统里没有cmake怎么办? 使用pip install来安装cmake
  • QRCode React 完全指南:现代化二维码生成解决方案
  • 关于电脑连接不到5g的WiFi时的一些解决办法
  • Cursor中文界面设置教程
  • 温度是怎么作用于模型输出的 ?
  • 一个迁移案例:从传统 IDC 到 AWS 的真实对比
  • DeerFlow实践:华为LTC流程的评审智能体设计
  • Linux内核调优实战指南
  • Debezium日常分享系列之:深入解析SQL Server事务日志
  • PostgreSQL——并行查询
  • CTFHub SSRF通关笔记10:DNS重绑定 Bypass 原理详解与渗透实战
  • Nginx 优化与防盗链实践
  • Altium Designer(AD)PCB丝印批量修改
  • MySQL在Centos 7环境下安装
  • MLLM学习~M3-Agent Prompt学习
  • ARM 架构的存储器模型
  • MongoDB C# .NetCore 驱动程序 序列化忽略属性
  • 【个人项目】【前端实用工具】OpenAPI到TypeScript转换工具 - 技术指南
  • 简单了解一下GraphRAG
  • 系统架构设计师——【2024年上半年案例题】真题模拟与解析(一)
  • LINUX中USB驱动架构—USB驱动程序框架
  • 【Web】ImaginaryCTF 2025 wp
  • [Windows] (思源笔记首发ai辅助工具)叶归 AI 辅助精美笔记工具
  • 多线程详解
  • ArcGIS(Pro)在线地图服务被禁?提示感叹号?应急方案来了——重新正常显示
  • 《PyTorch 携手 Unity:基于云原生架构化解 AI 游戏系统显存危机》
  • pytorch基本运算-Python控制流梯度运算