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

JavaEE(系列3) -- 多线程(线程的中断与线程等待)

 新内容开始之前,我们总结一个知识点.

Thread类中的start方法和run方法的区别?

start():

        用start方法来启动线程,真正实现了多线程运行,这时无需等待run方法体代码执行完毕而直接继续执行下面的代码。通过调用Thread类的start()方法来启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行, 然后通过此Thread类调用方法run()来完成其运行操作的, 这里方法run()称为线程体,它包含了要执行的这个线程的内容, Run方法运行结束, 此线程终止。然后CPU再调度其它线程。 一旦得到cpu时间片,就开始执行run()方法,这里方法 run()称为线程体,它包含了要执行的这个线程的内容,Run方法运行结束,此线程随即终止.

run():

        run()方法只是类的一个普通方法而已,如果直接调用Run方法,程序中依然只有主线程这一个线程,其程序执行路径还是只有一条,还是要顺序执行,还是要等待run方法体执行完毕后才可继续执行下面的代码,这样就没有达到写线程的目的。

总结:调用start方法方可启动线程,而run方法只是thread的一个普通方法调用,还是在主线程里执行。这两个方法应该都比较熟悉,把需要并行处理的代码放在run()方法中,start()方法启动线程将自动调用 run()方法,这是由jvm的内存机制规定的。并且run()方法必须是public访问权限,返回值类型为void。

目录

1. Thread 类的方法 

2. 线程中断

2.1 设置一个结束标志位

2.2 interrupt() (Thread 中自带的标志位) 

2.3 为什sleep要清空标志位?

3.线程等待


1. Thread 类的方法 

2. 线程中断

线程的中断,就是将一个线程停下来.

线程的终止:本质上只有一种,就是让该线程的入口方法执行完毕.

2.1 设置一个结束标志位

/*** Created with IntelliJ IDEA.* Description:线程中断1-自己创建变量,控制循环* User: YAO* Date: 2023-05-08* Time: 16:28*/
public class ThreadDemo7 {public static boolean isQuit = false;public static void main(String[] args) {Thread thread = new Thread(()->{while (!isQuit){System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("新线程结束");});thread.start();try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}isQuit = true;}
}

通过主线程进行修改标志位,使得新线程中的代码停止了循环.

思考:此时如果将isQuit,不写成成员变量,写在Main方法之中,还会达到想要的效果吗? 

答案:不可以 虽然lambda表达式能够访问局部变量,但是lambda表达式有变量捕获,捕获的变量必须是没有进行修改过的变量,main方法中后面对isQuit做了修改,所以不可以写在main方法中.这个变量要么是被final修饰,如果不是被final修饰的 你要保证在使用之前,没有修改. 

2.2 interrupt() (Thread 中自带的标志位) 

public class ThreadDemo8 {public static void main(String[] args) {Thread thread = new Thread(()->{//currentThread()  获取当前线程的实例while (!Thread.currentThread().isInterrupted()){System.out.println("hello t");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();//打印当前异常位置的调用栈}}});thread.start();try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}//将标志位设置为truethread.interrupt();}
}

简要介绍一下上面的代码.

首先认识两个方法:

1. Thread.currrentThread() :是获取当前线程的实例.

2. Thread.currrentThread() .isInterrupted()  --->默认是false

 

interrupt方法的作用:
* 1.设置标志位为true
* 2.如果该线程处于堵塞的状态(比如正在执行sleep),此时就会把阻塞状态给唤醒.通过抛出异常的方式让sleep立即结束.

当我们运行上述代码的时候,我们会看见下述问题  

 

出现的原因:

主线程中通过设置标志位interrupt,就立刻将sleep给唤醒,当sleep被唤醒的时候,sleep会自动的把isInterrupted标志位给清空  (true->flase),这样新线程的循环就被打通了,继续的执行sleep下一次遇见的时候就标志位就是false,那么就执行sleep本身,也不抛出异常,循环一直走下去,如果想要该循环截止,就在抛出异常的时候加一个break.

多线程的代码执行顺序不是“从上到下”,而是每个线程独立执行的。

2.3 为什sleep要清空标志位?

 清空标志位的目的是让线程本身能够对于线程何时结束,有一个更明确的控制.

当前的interrupt方法效果,并不是让线程立即结束,而是告诉他,你该结束了,至于他是否真的要结束,立即结束还是等会结束,都是代码来灵活控制的。

interrupt只是建议,不是命令。为什么java不强制设定为“命令结束”的操作呢?只要调用interru就立即结束呢?因为设定成这种,非常不友好。线程t何时结束,一定是t自己最清楚。交给t自己决定,就比较好。

3.线程等待

使用thread.join()

线程之间是并发执行的。操作系统对于线程的调度是无序的,无法判定两个线程谁先执行结束,谁后执行结束。

package threading;public class ThreadDemo8 {public static void main(String[] args) {Thread t=new Thread(()->{System.out.println("hello t");});t.start();System.out.println("hello main");}
}

 上述代码中,先输出hello main 还是先输出hello t是无法确定的,大概率先输出hello main.必定创建线程是需要开销的,但是我们海狮无法确定优先顺序,这就引出一个问题,当我们需要一个场景就是,必须等待线程2执行完了再执行线程1,这时候我们怎么办呢?

使用关键字 join

public class ThreadDemo9 {public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(()->{System.out.println("hello t");});thread.start();System.out.println("hello main1");//在main线程中调用thread.join,main线程等待Thread线程,等Thread执行完了之后,在继续执行main线程.thread.join();System.out.println("hello main2");}
}

上述代码的意思是,在主线程中加了一个t.join,就是等待t线程结束之后,再执行t.join之后的main线程中的代码.也就是在t线程结束之前,main线程处于一个堵塞的状态,暂时不参加CPU的调度执行.运行结果如下:

补充: 

join还有一个版本,可以填写一个参数,作为“超时时间”,等待的最大时间。

join的无参数版本,效果就是“死等”,不见不散。

join的有参数版本,则是执行最大超时时间,如果等待的时间到了上限,还没有等到,咱就不等了。

 那么此时就会联想到一个问题,会不会出现两个线程互相等待的情况.

 

答案:是会出现的 那种情况称为死锁,是bug。  比如家钥匙锁车里了,车钥匙锁家里了。

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

相关文章:

  • 想装一台自己的电脑,可以先了解下这些问题
  • Redis未授权漏洞复现
  • 跳槽,如果没有更好的选择,可以去美团试试···
  • Java10
  • IMS call通话类型对比差异
  • 5.2 中心极限定理
  • JVM 内存分哪几个区,如和判断一个对象是否存活
  • 在Spring Boot微服务使用Jedis操作Redis List列表
  • springboot + vue 部署 阿里云云服务器 ECS
  • mysql 日期 计算 时间差 天数差
  • 不用网闸、FTP的话 如何实现内外网数据交换?
  • 探寻Spring MVC的奥秘:内部组件与工作流程详解
  • eclipse svn ClassNotFoundException: javassist.ClassPool
  • 广度优先遍历搜索迷宫最短路径
  • 分布式计算基础知识
  • Mybatis方式完成CRUD操作
  • css背景 background的属性作用和值
  • 六大行文化特色知识(上)
  • 匿名对象的特性和使用场景你知道吗?
  • 企业应该如何做到数字化转型成功?
  • PBDB Data Service:Bibliographic references for fossil collections(采集记录参考书目)
  • 浅析图形验证码安全
  • 论文笔记:基于手机位置信息的地图匹配算法
  • 因果推断系列16-面板数据与固定效应
  • 第三十三章 弹性池塘2(弹城少年歌词)
  • PMP之预测部分
  • Node.js 异步流控制
  • 掌握这些思维技巧,解救996的打工人!
  • 【嵌入式Linux】MBR分区表 和 GPT分区表
  • 【华为OD机试真题】MVP争夺战(python)100%通过率 超详细代码注释 代码解读