线程的优先级、守护线程、礼让线程、插入线程、中断线程

2024-02-13 03:50

本文主要是介绍线程的优先级、守护线程、礼让线程、插入线程、中断线程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

 一. 线程的优先级

在计算机当中,线程的调度有两种方式:

  1. 抢占式调度模型多个线程它是在抢夺CPU的执行权,CPU在什么时候执行哪条线程是不确定的,执行多长时间也是不确定的,所以抢占式调度它体现了一个随机性。优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的 CPU 时间片相对多一些。
  2. 非抢占式调度 / 分时调度模型所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片 。那就是所有的线程轮流的执行,你一次我一次,你一次我一次,执行的时间也是差不多的
  • 在Java当中,它采取了第一种抢占式调度模型的方式。抢占式调度,重点掌握两个字随机性。
  • 假如计算机只有一个 CPU,那么 CPU 在某一个时刻只能执行一条指令,线程只有得到CPU时间片,也就是使用权,才可以执行指令所以说多线程程序的执行是有随机性,因为谁抢到CPU的使用权是不一定的。
  • 优先级越大,那么这条线程抢到CPU的概率就是最大的。
  • 在Java当中线程的优先级分为十档,最小的是一,最大的是十,如果没有设置默认就是五。
  • 优先级越高,只能保证它抢到CPU时间片的概率是最大的,但不能保证百分之百抢到CPU的执行权,它只是一个概率问题。
  • 线程的执行它是随机性的。

package com.gch.d2_api;
/**线程任务类*/
public class MyRunnable implements Runnable{@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName() + "----" + i);}}
}
package com.gch.d2_api;public class ThreadDemo03 {public static void main(String[] args) {/*setPriority(int newPriority)   设置线程的优先级final int getPriority          获取线程的优先级*/// 创建线程要执行的参数对象Runnable target = new MyRunnable();// 创建线程对象Thread t1 = new Thread(target,"飞机");Thread t2 = new Thread(target,"坦克");System.out.println(t1.getPriority()); // 5System.out.println(t2.getPriority()); // 5// 虚拟机自己创建的main线程的优先级,也是5System.out.println(Thread.currentThread().getPriority()); // 5t1.setPriority(1);t2.setPriority(10);t1.start();t2.start();}
}
  •  飞机的优先级为1,坦克的优先级为10,而飞机还先执行完!

二. 守护线程(备胎线程)

  •  当唯一运行的线程都是守护线程时,程序运行后Java虚拟机将直接退出!
  • 注意:setDaemon(true)方法必须在start()方法前执行,否则会抛出ILLegalThreadStateException异常。
  • 在守护线程中产生的新线程也是守护线程。

Test1: 

package com.gch.d9_thread_daemon;public class MyThread1 extends Thread {@Overridepublic void run() {for (int i = 0; i < 10; i++) {System.out.println(getName() + "@" +i);}}
}
package com.gch.d9_thread_daemon;public class MyThread2 extends Thread {@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(getName() + "@" + i);}}
}
package com.gch.d9_thread_daemon;import com.gch.d2_api.MyThread;public class ThreadDemo {public static void main(String[] args) {/*final void setDaemon(boolean on)   设置为守护线程细节:当其他的非守护线程(女神线程)执行完毕后,守护线程(备胎线程)会陆续结束通俗易懂:当女神线程结束了,那么备胎也没有存在的必要了*/Thread t1 = new MyThread1();Thread t2 = new MyThread2();t1.setName("女神");t2.setName("备胎");// 把第二个线程设置为守护线程(备胎线程)t2.setDaemon(true);t1.start();t2.start();}
}

 

 Test2:当唯一运行的线程都是守护线程时,程序运行后Java虚拟机将直接退出。

package com.gch.d9_thread_daemon;/**当唯一运行的线程都是守护线程时,程序运行后Java虚拟机将直接退出。*/
public class ThreadDemo2 {public static void main(String[] args) {// 1.创建线程对象Thread t = new Thread(new Runnable() {@Overridepublic void run() {try {Thread.sleep(50000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});t.setDaemon(true); // 设置成守护线程,程序运行后线程不会休眠,Java虚拟机将直接退出// 2.启动线程 启动该线程后,有三个线程:t线程 / main线程 / GC线程t.start();}
}

 

守护线程的应用场景:当把聊天都关闭了,传输文件也就没有存在的必要了!

 问题:主线程执行完,子线程也一定结束吗?

  • 答:这取决于子线程是否是守护线程,如果子线程是守护线程,它将在主线程退出时终止,但是,如果子线程是非守护线程,那么即使在主线程退出后,子线程仍将继续执行。

三. 礼让线程 / 出让线程

  • public static void yield():出让线程 / 礼让线程
  • 表示出让当前CPU的执行权
  • 只是尽可能地会让结果均匀一点儿,因为礼让后会重新去竞争CPU的执行权,有可能还是自己竞争到了!
package com.gch.d14_thread_yield;public class MyThread extends Thread{/*** 设置线程名* @param name:线程名*/public MyThread(String name) {super(name);}@Overridepublic void run() { // "飞机"  "坦克"for (int i = 1; i <= 100; i++) {System.out.println(getName() + "@" +i);// 表示出让当前CPU的执行权Thread.yield(); // 只是尽可能地会让结果均匀一点,因为礼让后会重新去竞争CPU的执行权,有可能还是自己竞争到了}}
}
package com.gch.d14_thread_yield;/**public static void yield():出让线程 / 礼让线程*/
public class ThreadDemo {public static void main(String[] args) {// 1.创建线程对象Thread t1 = new MyThread("飞机");Thread t2 = new MyThread("坦克");// 2.启动线程t1.start();t2.start();}
}

 

四. 插入线程 / 插队线程

package com.gch.d15_thread_join;public class MyThread extends Thread {/*** 设置当前线程名* @param name:线程名*/public MyThread(String name) {super(name);}public MyThread(){}@Overridepublic void run() {for (int i = 1; i <= 100; i++) {System.out.println(getName() + "@" + i);}}
}

package com.gch.d15_thread_join;/**public final void join():插入线程 / 插队线程join()方法是指,在一个a线程中,被另外一个b线程强行插队,那么就必须要等到b线程执行完成以后,才能回到a线程执行任务!join(毫秒时间)方法是指a线程可以让b线程插队一定的时间,超过这个时间,又公平去竞争CPU的执行权!*/
public class ThreadDemo {public static void main(String[] args) throws InterruptedException {// 1.创建线程对象Thread t = new MyThread("土豆");// 2.开启线程t.start();// 表示把t这个线程,插入到当前线程之前// t:土豆,子线程// 当前线程:main线程,主线程// 把子线程插入到主线程之前,等子线程里面所有的代码全部执行完了,才会轮到主线程去执行!t.join(); // 完全执行完毕
//        t.join(10); // 可以传入一个毫秒数,最多可以让你插队多少时间// 执行在main线程当中的for (int i = 0; i < 10; i++) {System.out.println("main线程执行" + i);}}
}
  • 如下图,把子线程插入到主线程之前,等子线程里面所有的代码全部执行完了,才会轮到主线程去执行!
  • 总结:哪个线程调用了join(),哪个线程就先执行完毕,再执行
  • join()方法是指,在一个a线程中,被另外一个b线程强行插队,那么就必须要等到b线程执行完成以后,才能回到a线程执行任务!
  • join(毫秒时间)方法是指a线程可以让b线程插队一定的时间,超过这个时间,又公平去竞争CPU的执行权!

 

五. 中断线程 

  • public final void stop() ,直接调用线程的stop方法强制终止这个线程,此方法已经由于可能不安全已经被弃用了,可能造成死锁,改用interrupt()

  1. public void interrupt():设置中断标记位:中断这个线程方法,不是真的中断,只是去设置一个中断的标记为true,是否需要真的中断,需要捕获run方法抛出的异常,人工处理。 或者在run方法中判断,早点结束run方法。

  2. interrupt()只是改变中断状态而已,interrupt()不会中断一个正在运行的线程,这一方法实际上完成的是,给受阻塞的线程抛出一个中断信号。

  • 一旦设置中断标记位,那么只要在这个线程中遇到了wait / join / sleep这三个方法中的任何一个,它就会让这个线程抛出一个中断异常,抛出中断异常后,我们可以自行用return方法提前结束线程,来达到中断的效果,这才是真正让线程中断的办法。

  • 设置中断标记位这个方法的调用一定要在线程启动之后,才能生效!

  1. // 获取线程的中断标记状态后,并清除中断状态

    public static boolean interrupted() {return currentThread().isInterrupted(true);}

  2. // 只获取线程的中断标记状态,不会去清除中断状态。用Thread.currentThread()去调用这个方法,直接用线程对象调用会报错,报该变量未初始化。

     public boolean isInterrupted() {return isInterrupted(false);}

package com.gch.d16_thread_interrupt;/*** 还是打饭的这个例子:饭盒在打饭的过程中掉了、打饭过程中睡着了* 我们其实没有办法让线程中断,只能通过设置一个标记位,来让线程可以通过一定的方式去中断线程,最好的方式就是让线程在执行的过程中抛出异常,来中断线程。* public void interrupt():设置中断标记位:中断这个线程方法,不是真的中断,只是去设置一个中断的标记为true,是否需要真的中断,*   需要捕获run方法抛出的异常,人工处理。 或者在run方法中判断,早点结束run方法。* 小结:*     1.注意:中断标记位的设置,一定是在线程启动之后*     2.中断方法不是真的中断线程,只是设置一个中断的标记位为true,遇到wait/join/sleep方法的时候会抛出中断异常。*     3.抛出中断异常后,自行用return方法提前结束线程,来达到中断的效果。*  // 获取线程的中断标记状态后,并清除中断状态*  public static boolean interrupted() {return currentThread().isInterrupted(true);}*  // 只获取线程的中断标记状态,不会去清除中断状态。
用Thread.currentThread()去调用这个方法,直接用线程对象调用会报错,报该变量未初始化。*  public boolean isInterrupted() {return isInterrupted(false);}*/
public class ThreadDemo {public static void main(String[] args) {// 1.创建线程对象Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {System.out.println("开始打饭");try {
//                    boolean interrupted1 = Thread.interrupted(); // 获取线程的中断标记状态后,清除中断状态
//                    System.out.println("线程是否中断:" + interrupted1);
//
//                    boolean interrupted2 = Thread.interrupted(); // 获取线程的中断标记状态后,清楚中断状态
//                    System.out.println("线程是否中断:" + interrupted2);// 只是获取线程的中断标记状态boolean isInterrupted = Thread.currentThread().isInterrupted();System.out.println("线程是否中断:" + isInterrupted);Thread.sleep(1000);} catch (InterruptedException e) {
//                    throw new RuntimeException(e); 把抛出的异常注释掉,加了return提前结束run方法,就好比是中断了,这才是真正让线程中断的办法。return; // 提前结束这个任务,就好比是中断了,这才是真正让线程中断的办法。}System.out.println("开始打饭结束");}},"打饭线程");// 2.启动线程t1.start(); // 启动线程并不代表run方法就立马执行,它只是说线程处于一种就绪的状态,准备好去抢占CPU的执行权了!// 一旦设置中断标记位的话,那么只要在这个线程中遇到了这三个方法,那么它就会抛出异常// 只要调用wait,join,sleep这三个方法中的任何一个,它就会让这个线程抛出一个中断异常,// 抛出异常之后,我们人为的在异常中去处理即可让线程中断// 这就是我们真正让线程中断的最佳办法t1.interrupt(); // 设置中断标记位:[注意:设置中断标记位这个方法的调用一定要在线程启动之后,才能生效!]}
}

这篇关于线程的优先级、守护线程、礼让线程、插入线程、中断线程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/704419

相关文章

Springboot的ThreadPoolTaskScheduler线程池轻松搞定15分钟不操作自动取消订单

《Springboot的ThreadPoolTaskScheduler线程池轻松搞定15分钟不操作自动取消订单》:本文主要介绍Springboot的ThreadPoolTaskScheduler线... 目录ThreadPoolTaskScheduler线程池实现15分钟不操作自动取消订单概要1,创建订单后

C语言线程池的常见实现方式详解

《C语言线程池的常见实现方式详解》本文介绍了如何使用C语言实现一个基本的线程池,线程池的实现包括工作线程、任务队列、任务调度、线程池的初始化、任务添加、销毁等步骤,感兴趣的朋友跟随小编一起看看吧... 目录1. 线程池的基本结构2. 线程池的实现步骤3. 线程池的核心数据结构4. 线程池的详细实现4.1 初

Java子线程无法获取Attributes的解决方法(最新推荐)

《Java子线程无法获取Attributes的解决方法(最新推荐)》在Java多线程编程中,子线程无法直接获取主线程设置的Attributes是一个常见问题,本文探讨了这一问题的原因,并提供了两种解决... 目录一、问题原因二、解决方案1. 直接传递数据2. 使用ThreadLocal(适用于线程独立数据)

第10章 中断和动态时钟显示

第10章 中断和动态时钟显示 从本章开始,按照书籍的划分,第10章开始就进入保护模式(Protected Mode)部分了,感觉从这里开始难度突然就增加了。 书中介绍了为什么有中断(Interrupt)的设计,中断的几种方式:外部硬件中断、内部中断和软中断。通过中断做了一个会走的时钟和屏幕上输入字符的程序。 我自己理解中断的一些作用: 为了更好的利用处理器的性能。协同快速和慢速设备一起工作

顺序表之创建,判满,插入,输出

文章目录 🍊自我介绍🍊创建一个空的顺序表,为结构体在堆区分配空间🍊插入数据🍊输出数据🍊判断顺序表是否满了,满了返回值1,否则返回0🍊main函数 你的点赞评论就是对博主最大的鼓励 当然喜欢的小伙伴可以:点赞+关注+评论+收藏(一键四连)哦~ 🍊自我介绍   Hello,大家好,我是小珑也要变强(也是小珑),我是易编程·终身成长社群的一名“创始团队·嘉宾”

线程的四种操作

所属专栏:Java学习        1. 线程的开启 start和run的区别: run:描述了线程要执行的任务,也可以称为线程的入口 start:调用系统函数,真正的在系统内核中创建线程(创建PCB,加入到链表中),此处的start会根据不同的系统,分别调用不同的api,创建好之后的线程,再单独去执行run(所以说,start的本质是调用系统api,系统的api

web群集--nginx配置文件location匹配符的优先级顺序详解及验证

文章目录 前言优先级顺序优先级顺序(详解)1. 精确匹配(Exact Match)2. 正则表达式匹配(Regex Match)3. 前缀匹配(Prefix Match) 匹配规则的综合应用验证优先级 前言 location的作用 在 NGINX 中,location 指令用于定义如何处理特定的请求 URI。由于网站往往需要不同的处理方式来适应各种请求,NGINX 提供了多种匹

java线程深度解析(六)——线程池技术

http://blog.csdn.net/Daybreak1209/article/details/51382604 一种最为简单的线程创建和回收的方法: [html]  view plain copy new Thread(new Runnable(){                @Override               public voi

java线程深度解析(五)——并发模型(生产者-消费者)

http://blog.csdn.net/Daybreak1209/article/details/51378055 三、生产者-消费者模式     在经典的多线程模式中,生产者-消费者为多线程间协作提供了良好的解决方案。基本原理是两类线程,即若干个生产者和若干个消费者,生产者负责提交用户请求任务(到内存缓冲区),消费者线程负责处理任务(从内存缓冲区中取任务进行处理),两类线程之

java线程深度解析(四)——并发模型(Master-Worker)

http://blog.csdn.net/daybreak1209/article/details/51372929 二、Master-worker ——分而治之      Master-worker常用的并行模式之一,核心思想是由两个进程协作工作,master负责接收和分配任务,worker负责处理任务,并把处理结果返回给Master进程,由Master进行汇总,返回给客