线程等待,线程休眠,线程状态

news2025/1/17 4:54:02

线程等待:因为线程与线程之间,调度顺序是完全不确定,它取决于操作系统本身调度器的一个实现,但是有时候我们希望这个顺序是可控的,此时的线程等待,就是一种方法,用来控制线程结束的先后顺序;

1)我们的线程之间,调度顺序是不确定的,线程之间的执行是按照调度器来进行安排执行的,这个过程是无序,随机的,有些时候,但是这样不太好,我们要控制线程之间的执行顺序,先进行执行线程1,再来执行线程2,再来执行线程3;

2)线程等待就是其中一种控制线程执行的先后顺序的一种手段,此处我们所说的线程等待,就是我们说的控制线程结束的先后顺序

3)当我们进行调用join的时候,哪个线程调用的join,那个线程就会阻塞等待,直到对应的线程执行完毕,也就是对应线程run方法执行完之后,

4)我们在调用join之后,对应线程就会进入阻塞状态,暂时这个线程无法在CPU上面执行,就暂时停下了,不会再继续向下执行了,就是让main线程暂时放弃CPU,暂时不往CPU上面调度,往往会等待到时机成熟

5)Sleep等待的时机是时间结束,而我们的join等待的时机是当我们的(t.join),t线程中的run方法执行完了,main方法才会继续执行

public class Main{
    public static void main(String[] args) throws InterruptedException {
        Thread t1=new Thread(()->{
          for(int i=0;i<5;i++){
              System.out.println("hello Thread");
              try {
                  Thread.sleep(1000);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
          }
        });
        t1.start();
//我们在主线程中就可以使用一个等待操作来进行等待t线程执行结束
        try{
            t1.join();
        }catch(InterruptedException e){
            e.printStackTrace();
        }
    }
}

当我们的join执行到这一行,就暂时停下了,不会继续向下执行,当前的join方法是main方法调用的,针对这个t线程对象进行调用的,此时就是让main等待t,我们这是阻塞状态,调用join之后,main方法就会暂时进入阻塞状态(暂时无法在CPU上执行);

6)join默认情况下是死等,只要对应的线程不结束,那么我们就进行死等,里面可以传入一个参数,指定时间,最长可以等待多久,等不到,咱们就撤

Thread.currentThread()表示获取当前对象的实例,相当于在Thread实例中run方法中直接使用this

操作系统管理线程:
1)描述
2)组织:双向链表
就绪队列:队列中的PCB有可能随时被操作系统调度上CPU执行

1)我们要注意,当前这几个状态,都是Thread类的状态和操作系统中的内部的PCB的状态并不是一致的;

2)然后我们从当前join位置啥时候才可以向下执行呢?也就是说恢复成就绪状态呢?就是我们需要等待到当前t线程执行完毕,也就是说t的run方法执行完成了,通过线程等待,我们就可以让t先结束,main后结束,一定程度上干预了这两个线程的执行顺序

3)我们此时还是需要注意,优先级是咱们系统内部,进行线程调度使用的参考量,咱们在用户代码层面上是控制不了的,这是属于操作系统内核的内部行为,但是我们的join是控制线程代码结束之后的先后顺序

4)就是说我们带有参数的join方法的时候,就会产生阻塞,但是这个阻塞不会一直执行下去,如果说10s之内,t线程结束了,此时的join会直接进行返回,但是假设我们此时的10S之后,t线程仍然不结束,那么join也直接返回,这就是超时时间

sleep和yield有什么区别?

sleep和yield都是Thread类中的静态方法,yield是暂停当前执行的线程对象,就是放弃当前拥有的CPU资源,并执行其他线程,就是让当前运行的线程回到就绪状态,也就是可执行状态,就是以保证相同优先级的状态具有执行机会

1)sleep()方法给其他线程运行机会时不考虑线程的优先级,因此会给优先级低的线程以运行的机会,而yield()方法只会给相同优先级或者更高优先级的线程以运行机会,yield(),虽然不会经常用到,让线程主动让出CPU,但是不会改变线程的状态
2)线程执行sleep()方法后会转入阻塞状态,所以,执行sleep()方法的线程在指定的时间内肯定不会被执行,而yield()方法只是使当前线程重新回到可执行状态,所以执行yield()方法的线程有可能在进入到可执行状态后马上又被执行。
3)sleep()方法声明抛出InterruptedException,而yield()方法没有声明任何异常;

获取线程的名字 

1)Thread.currentThread()的意思就是说这个方法就可以获取到当前线程的引用,也就是说Thread实例的引用,哪一个线程调用的currentThread,就获取到的是那一个线程的实例

2)Thread.currentThread的操作等同于this,但是我们无法在Runnable和lmada表达式里面进行使用this,连代码都编译不了

 

3)如果说我们在main线程里面调用Thread.currentThread()这个时候调用的就是main线程的实例

 public static void main(String[] args) {
          Thread t=new Thread(()->{});
          t.start();
          System.out.println(Thread.currentThread().getName());
//当前我们调用Thread.currentThread()方法的是main线程,我们先通过这个方法获得该实例对象的引用,通过引用就可以访问到该方法
    }
打印结果:main

 

线程休眠:sleep

操作系统是如何管理进程的?是通过一个类来进行描述的,通过一个双向链表来进行组织的,这个说法可以是说针对只有一个线程的进程,是如此的,但是如果说一个进程里面有多个线程,每一个线程里面都有一个PCB,一个进程对应的就是一组PCB;

1)咱们的上面的这个双向链表属于就绪队列,但是在我们的操作系统内核里面,这样的队列却不是有多个,如果说某一个线程调用了sleep方法,这个PCB就会进入到阻塞队列 

2)咱们的操作系统进行调度线程的时候,就是从就绪队列里面挑选合适的PCB到CPU上面运行,那么我们阻塞队列里面的PCB就只能干等着,啥时候这个PCB可以回到就绪队列里面呢?那么只有说睡眠时间到了,咱们的系统才会把刚才的这个PCB从阻塞队列挪回到就绪队列里面

3)只有我们就绪队列里面的PCB才有被调度上CPU执行的权力,阻塞队列里面的PCB没有资格进入到CPU上面执行

1)我们的一个进程对应的就是一组PCB,每一个PCB上面就有一个字段叫做tgroupID,这个ID其实就是进程的tgroupID,同一个进程中的若干个线程的tgroupID其实是相同,linux的系统内核是不区分进程和线程的,linux内核只认PCB,进程和线程其实是咱们程序员写有关于应用程序的代码才搞出来的词,实际上linux内核只认PCB,在linux系统内核里面我们把线程称之为轻量级进程,只不过是有些PCB共用同一个内存,有些PCB共用同一块虚拟地址空间,有些PCB有不同的虚拟地址空间,我们前面所说的进程线程概念是站在一个更加抽象的角度,站在用户写代码的角度来进行看待的,但在操作系统内核实现的角度,他们是一视同仁,使用同样的方式来进行表述的;

2)咱们的上面的这个链表就是就绪队列的双向链表,如果我们的某个线程调用了sleep方法,这个PCB就会进入到阻塞队列,实际上,咱们的操作系统在进行调度线程的时候,就是从我们的就绪队列中查找合适的PCB到我们的CPU上面执行,当我们的睡眠时间到了,系统就会把刚才这个PCB从阻塞队列挪回到就绪队列,join也会导致线程进入到阻塞队列里面

线程状态(通过线程的引用的GetState()方法来获取到线程状态)

1)在我们的开发中会经常遇到线程卡死的情况,有一些关键的线程阻塞了,线程卡死了,在我们分析原因的时候,我们就可以看看当前的各种线程所处的状态

2)this.getState();Thread.currentThread().getState();

3)进程的状态就是系统按照什么样子的态度来进行决定调度这个进程,就绪态和阻塞态相对于那种一个进程只有一个线程的情况,进程中单线程的状态就绑定在线程的身上,更常见的情况下,一个进程中包含了多个线程,所谓的状态,就是绑定在了线程上面

4)在linux中,咱们的PCB其实是和线程相对应的,一个进程对应着一组PCB,我们通过进程的状态来进行区分决定当前进程是否要被调度上CPU进行执行

5)我们上面所说的就绪状态和阻塞状态都是针对系统上面的线程的状态,也就是PCB的状态

6)在我们的JAVA里面,在Thread类里面,我们针对线程的状态,又再次进行了划分

7)我们的线程的状态,本质上来说是一种枚举类型

public class Solution {
    public static void main(String[] args) {
        for(Thread.State state:Thread.State.values()){
            System.out.println(state);
        }
    }
}
NEW
RUNNABLE
BLOCKED
WAITING
TIMED_WAITING
TERMINATED

使用JAVA来进行打印线程的所有状态: 

public class Teacher {
    public static void main(String[] args) {
        for(Thread.State value:Thread.State.values()){
            System.out.println(value);
        }
    }
}

也就是说这些状态是JAVA自己搞出来的,就和操作系统中的PCB的状态没有啥关系

1)NEW:Thread对象有了,内核中的PCB还没有创建,相当于任务布置了,但是还没有开始执行工作,没有调用start方法;

2)RUNNABLE:调用了run方法之后,就绪状态,就是在就绪队列里面,当前的PCB已经创建出来了,当前线程正在CPU上执行或者已经准备好上CPU上执行了,如果我们的代码中没有进行sleep,也没有出现其他的可能导致阻塞的操作,代码大概率是出于Runnable状态的;

3)BLOCKED:排队等待synchronized状态导致阻塞,等待锁状态阻塞状态,线程中尝试进行加锁,发现锁已经被被其他线程调用了,那么此时该线程也会进行阻塞等待,这个等待就会等到其他线程释放锁之后(synchronized)(获取到锁没有获取成功)

4)WAITTING:线程中调用wait()方法,没有等待时间,死等除非有其他线程来唤醒

5)TIME-WAITTING:表示当前PCB在阻塞队列中等待呢意思就是当前线程在一定时间之内是处于阻塞的状态,一定时间到了之后,阻塞状态解除,当我们的代码执行了sleep(时间)就会进入到TIMED_WAITING,还有join(等待时间),在调用的时候,都会进入到阻塞状态;

注释:3,4,5状态都属于阻塞状态,只是阻塞的条件不同,当前的线程暂时停了下来,不会到CPU上继续执行,等到时机成熟才回到CPU上执行

6)TERMINATED状态:此时内核中的线程已经完成了任务,PCB没了,线程中被销毁,但是代码中的Thread对象还在,这时就得等这个对象被GC回收;这里我们就知道了,Thread对象与内核中的PCB的生命不一样

islive判断内核中的线程是否存在,除了1和6之外,结果都是true(判断内核中的PCB是否存在)

1)当我们进行调用wait()方法之后,就会从runnable变成waitting状态

2)当我们的线程获取到synchronized锁之后,就会从Blocked状态变成runnable状态

 public static void main(String[] args) throws InterruptedException {
       Thread thread=new Thread(){
           int count=0;
         public void run(){
             while(true) {
                 count++;
                 System.out.println(Thread.currentThread().getState());//RUNNABLE状态
                 if (count == 10) {
                     break;
                 }
             }
         }
       };
       thread.start();
       Thread.sleep(1000);
       System.out.println(thread.getState());//Terminated状态
    }

1)Blocked,Waitting,TimeWaitting都是处于阻塞状态

2)在我们进行分析卡死原因的时候,我们就可以来进行分析一下当前程序中各种关键线程所处的状态,TimeWaitting,你这里面是不是Sleep,Blocked状态,是不是在处于等待锁的状态,是不是死锁,Waitting是不是说代码中哪里调用了wait或者是join,没人来进行唤醒;远离就解释了代码为什么这个写,这个BUG背后的原因可以解释清楚;

常用方法:

public static void main(String[] args)throws InterruptedException{
        Thread t1=new Thread(){
            public void run() {
                for(int i=0;i<10;i++)
                {
                   // System.out.println(Thread.currentThread().getName());
                }
                try{
                    sleep(100);
                }catch(InterruptedException e)
                {
                    e.printStackTrace();;
                }
    System.out.println("线程结束");
            }};
        System.out.println(t1.getName());//获取线程名字
        System.out.println(t1.getPriority());//获线程优先级
        System.out.println(t1.isDaemon());//该线程是否为守护线程
        System.out.println(t1.getId());
        System.out.println(t1.isAlive());
        System.out.println(t1.isInterrupted());
        System.out.println(t1.getState());//获取到指定线程的状态
        t1.start();
        while(t1.isAlive()){
            System.out.println(t1.getState());
            System.out.println(t1.isInterrupted());
        }
        System.out.println(t1.getState());
    }}

我们竖着下来的这一连串属于主线任务,剩下两边的都是属于支线任务,我们创建出一些线程,就是让他执行一些任务的,可能我们在Runnable过程中执行一些特殊代码导致线程切换到了其它的一些阻塞状态,不同的代码就会进入到不同的一个阻塞情况

1)从NEW状态到Runnable状态:当我们创建一个线程的时候,new一个Thread对象的时候,就会进入到NEW状态,当我们调用Start方法的时候,就会从NEW状态到Runnable状态

2)从Runnable状态到BLOCKED状态:当我们线程中的代码排队执行synchronized的时候,线程就会从RUNNABLE状态变成BLOCKED

3)当我们的线程进行获取到synchronized锁的时候,就会从BLOCKED状态变成RUNNABLE状态

4)从Runnable状态到waitting状态:当我们的程序中调用wait()方法之后,就会从RUNNABLE状态变成WAITTING变成无限等待状态,但是当我们调用notify/notifyAll方法,就会从waitting状态变成runnable状态

5)从Runnable状态到TIMED_WAITTING:当我们调用超时时间的等待方法的时候,比如说sleep方法,会从RUNNABLE状态变成TimeWaitting状态,当过了超时时间之后,线程就会从timewaitting状态变成runnable状态

6)当我们从RUNNABLE状态变成Terminated状态,就是PCB已经被销毁

 总结:

1)线程是一个独立的执行流,主要他是为了解决并发编程的问题,虽然多进程也可以,但是不如线程更轻量

2)创建进程比创建线程开销大很多

销毁进程比销毁线程开销大很多

调度进程比调度线程开销大很多

3)Thread类就是JAVA中系统对于多线程API提供的封装

3.1)构造方法创建线程,继承于Thread(匿名内部类,lamda表达式),实现Runnable接口(匿名内部类,lamda表达式),在构造方法里面还可以指定线程的名字,区分当前线程是哪一个

3.2)创建线程,在系统内里面创建,通过start方法进行创建,只有调用start方法,才可以在系统中真正创建出一个线程,run方法只是描述了一个线程在干什么,只有咱们的start方法才是在系统中真正的进行创建出了一个PCB

4)中断线程:核心思路就是说让我们的当前的run方法执行完成,本质上来说就是从while循环里面指定一个条件,条件不满足,循环结束,此时的run方法也就完了,同时我们也是可以借助Thread类中内置的提供的标志位;

4.1)我们通过调用线程的Interrupt方法来去出发终端操作,这个方法又有两种行为,要么是设置标志位,当我们的线程处于就绪状态的时候,也就是我们的PCB处于就绪队列的时候,要么是抛出一个异常,当前成处于阻塞状态的时候,直接就抛出异常

5)等待线程:join,让一个线程等待另一个线程结束,让一个线程先结束,然后自己后结束,会让调用join的线程产生阻塞效果;等到我们的t.join(),直到t执行完成之后,才会返回

6)线程休眠

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/162049.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

神经网络基础部件-激活函数详解

本文分析了激活函数对于神经网络的必要性&#xff0c;同时讲解了几种常见的激活函数的原理&#xff0c;并给出相关公式、代码和示例图。 一&#xff0c;激活函数概述 1.1&#xff0c;前言 人工神经元(Artificial Neuron)&#xff0c;简称神经元(Neuron)&#xff0c;是构成神经…

[linux] 冯诺依曼体系及操作系统的概念

文章目录1. 冯诺依曼体系结构1. 为什么要有内存&#xff1f;1. 若内存不存在2. 若内存存在结论12.在硬件层面&#xff0c;单机和跨单机之间数据流是如何流向的&#xff1f;结论22. 操作系统(Operator System)1. 概念2.如何理解操作系统对硬件管理&#xff1f;结论13.管理者和被…

Linux安装 MySQL

1、MySQL安装方式 Linux MySQL安装有很多方式&#xff1a;yum安装、apt-get安装、rpm安装、二进制安装、源码编译安装。 比较通用的做法就是&#xff0c;二进制安装、源码编译安装&#xff0c;但是源码编译安装太麻烦&#xff0c;所以一般都是选择二进制安装。本文就是采用二…

百度百科创建词条参考资料问题汇总

百度百科词条编辑规则是相当复杂的&#xff0c;不是单纯写一写百度词条内容那么简单&#xff0c;还需要准备对应的参考资料来佐证你内容的真实性&#xff0c;很多小伙伴就因为这个参考资料犯了难&#xff0c;每次词条审核不通过的原因也大部分是因为参考资料的问题。 参考资料…

爬虫攻守道 - 2023最新 - Python Selenium 实现 - 数据去伪存真,正则表达式谁与争锋 - 爬取某天气网站历史数据

前言 前面写过3篇文章&#xff0c;分别介绍了反爬措施&#xff0c;JS逆向ajax获取数据&#xff0c;以及正则表达式匹配开头、结尾、中间的用法。第3篇算是本文 Python Selenium 爬虫实现方案的子集&#xff0c;大家可以参照阅读。 另外本意是“攻守”&#xff0c;不知道为何输…

【搞懂AUTOSAR网络管理测试】AUTOSAR网络管理规范需求解读

文章目录前言一、名词解释二、NM报文1.NM报文格式2.NM报文数据场内容三、NM状态机1.NM状态转换图2.状态前言 AUTOSAR Automotive Open System Architecture&#xff0c;汽车开放系统架构&#xff0c;由全球汽车制造商、部件供应商及其他电子、半导体和软件系统公司联合建立&am…

【Nginx】Nginx配置实例-负载均衡

1. 首先准备两个同时启动的 Tomcat2. 在 nginx.conf 中进行配置 1. 首先准备两个同时启动的 Tomcat 2. 在 nginx.conf 中进行配置 在 nginx.conf 中进行配置 随着互联网信息的爆炸性增长&#xff0c;负载均衡&#xff08;load balance&#xff09;已经不再是一个很陌生的话题…

中睿天下入选“2023年网络安全服务阳光行动”成员单位

近日&#xff0c;中国网络安全产业联盟&#xff08;CCIA&#xff09;正式公布了“2023年网络安全服务阳光行动”成员单位名单。中睿天下作为以“实战对抗”为特点的能力价值型网络安全厂商&#xff0c;凭借领先的产品方案、专业的服务支持和优秀的行业自律精神成功入选。为规范…

12、Javaweb_登录添加删除修改多选删除分页查询复杂条件查询案例

1. 综合练习 1. 简单功能 1. 列表查询 2. 登录 3. 添加 4. 删除 5. 修改 2. 复杂功能 1. 删除选中 2. 分页查询 * 好处&#xff1a; 1. 减轻服务器内存的开销 …

C/C++数据类型转换详解

目录 C语言数据类型转换 1、自动类型转换 &#xff08;1&#xff09;算术表达式的自动类型转换 &#xff08;2&#xff09;赋值运算中的自动类型转换 2、强制类型转换 C数据类型转换 1、static_cast<> 2、const_cast<> 3、dynamic_cast<> 4、reint…

【Allegro软件PCB设计120问解析】第78问 如何在PCB中手动添加差分对及自动添加差分对属性呢?

答:设计PCB过程中,若设计中有差分对信号,则需要将是差分的2个信号设置为差分对,设置差分对有2种方式:手动添加及自动添加。 1、 手动添加差分对: 第一步,点击Setup-Constraints-Constraint Manager调出CM规则管理器,然后到Physical规则管理器下点击Net-All Layers,然…

React脚手架+组件化开发+组件生命周期+组件通信

react脚手架&#xff08;create-react-app&#xff09; 1.作用&#xff1a; 帮助我们生成一个通用的目录结构&#xff0c;并且已经将我们所需的工程环境配置好 2.依赖环境 脚手架都是使用node编写的&#xff0c;并且都是基于webpack的&#xff1b; 3.安装node 4.安装脚手架 n…

在哔站黑马程序员学习Spring—Spring Framework—(一)核心容器(core container)学习笔记

一、spring介绍 spring是一个大家族全家桶&#xff0c;spring技术是JavaEE开发必备技能。spring技术解决的问题&#xff1a;一是简化开发&#xff0c;降低企业级开发的复杂性。二是框架整合&#xff0c;高效整合其他技术&#xff0c;提高企业级应用开发与运行效率。 spring技术…

git revert和git reset的差异点和区别

git revert 定义 撤销某次提交,此次撤销操作和之前的commit记录都会保留。 git revert会根据commitid找到此次提交的变更内容&#xff0c;并撤销这些变更并作为一次新commit提交。 ps:此次commit和正常commit相同&#xff0c;也可以被revert revert和reset有本质的差别&#…

使用C#开发ChatGPT聊天程序

使用C#开发ChatGPT聊天程序 总体效果如下&#xff1a; 源码下载 关键点1&#xff1a;无边框窗口拖动 Window中设置AllowsTransparency"True"、 Background"Transparent"以及WindowStyle"None"这样设置的话默认窗口的标题栏为不可见状态&…

cocos tween

缓动接口Tween 属性和接口说明接口说明接口功能说明tag为当前缓动添加一个数值类型&#xff08;number&#xff09;的标签to添加一个对属性进行 绝对值 计算的间隔动作by添加一个对属性进行 相对值 计算的间隔动作set添加一个 直接设置目标属性 的瞬时动作delay添加一个 延迟时…

Spring Cloud服务发现组件Eureka

简介 Netflix Eureka是微服务系统中最常用的服务发现组件之一&#xff0c;非常简单易用。当客户端注册到Eureka后&#xff0c;客户端可以知道彼此的hostname和端口等&#xff0c;这样就可以建立连接&#xff0c;不需要配置。 Eureka 服务端 添加Maven依赖&#xff1a; <…

菜谱分享APP/基于android菜谱分享系统

摘 要随着现代生活水平的不断提升&#xff0c;人们越来越关注健康,关注美食,大部分人都希望吃得美味的同时也要吃得健康,所以,有的人喜欢在家自己动手制作美食,但是却缺少这方面的资讯来源。菜谱分享APP是一个使用Hbuildex作为手机客户端和后台服务系统的开发环境, MySQL作为后…

Node.js+Vue.js全栈开发王者荣耀手机端官网和管理后台(一)

文章目录【全栈之巅】Node.jsVue.js全栈开发王者荣耀手机端官网和管理后台(一)工具安装和环境搭建初始化项目基于ElementUI的后台管理基础界面搭建创建分类&#xff08;客户端&#xff09;创建分类&#xff08;服务端&#xff09;分类列表分类编辑分类删除子分类【全栈之巅】No…

Bubbles原理解析

官方文档 https://developer.android.com/develop/ui/views/notifications/bubbles#the_bubble_api 气泡使用户可以轻松查看和参与对话。 气泡内置于通知系统中。 它们漂浮在其他应用程序内容之上&#xff0c;无论用户走到哪里都会跟随他们。 气泡可以展开以显示应用程序功能和…