首页 > 其他分享 >Thread类中的常用线程调度方法sleep、yield、join

Thread类中的常用线程调度方法sleep、yield、join

时间:2023-06-23 09:55:51浏览次数:38  
标签:调用 join Thread 执行权 yield 线程 sleep

sleep

sleep方法是在Thread类中的一个静态方法,当一个线程调用了sleep方法,被调用的那个线程就会暂时的让出指定时间的CPU执行权,在这段时间也不会参与CPU的调度,当时间到了之后,就会重新回到就绪状态,等待CPU的再次调度,注意是就绪状态,而不是重新拿回CPU的执行权。并且,在休眠期间,只是会让出CPU的执行权,但是之前获得的锁资源,还是继续持有,等CPU调度到该线程重新获取到执行权的时候,就会继续运行sleep之后的代码。接下来用一个例子来说明Sleep期间不会放弃锁资源

public class SleepDemo {
    public static void main(String[] args) throws InterruptedException {
        Lock lock = new ReentrantLock();
        Thread one = new Thread(() -> {
            lock.lock();
            System.out.println("线程A准备被Sleep"); //1
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程A被唤醒"); //2
            lock.unlock();
        });
        Thread two = new Thread(() -> {
            lock.lock();
            System.out.println("线程B准备被Sleep"); //3
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程B被唤醒"); //4
            lock.unlock();
        });
        one.start();
        two.start();
    }
}

在上面的代码中,new了两个线程,但是只有一个锁资源lock,如果sleep期间会释放锁资源的话,那么输出结果应当是交替输出的,因为在执行代码1的时候,会被剥夺执行权,那么线程2将会拿到锁,但是事实真的是这样吗,看输出结果

线程A准备被Sleep
线程A被唤醒
线程B准备被Sleep
线程B被唤醒

可以看到,线程A被sleep的时候,线程2执行,想要获取锁I资源lock,但是发现lock被占有了,只好阻塞,然后线程1的休眠时间到了,重新获取执行权,继续执行sleep之后的代码,然后释放锁,然后线程2拿到锁,执行后续代码。通过上面的代码就可以看出,sleep期间是不会释放锁资源的,只是暂时的让出了cpu的执行权。

yield

yield和sleep在功能上有点像。yield方法是Thread中的一个静态方法,当一个线程调用yield方法的时候,则会直接让出自己cpu执行权,并且直接到就绪状态,也就是说,调用yield不会让线程阻塞。举个通俗点的例子,我们都知道,操作系统调度线程的时候,会有一个时间片机制,当时间片用完之后,就会回到就绪状态等待cpu的下一次调度,如果A线程在他的时间片还没用完之前,调用了yield方法,就代表着,放弃还未用完的时间片,告诉线程调度器,还有多的时间片我不想用了,你直接进行下一轮调度吧,然后A线程也回到了就绪状态。请注意,调用了yield的线程,是回到了就绪状态,而不是阻塞,这意味着,即便他刚调用玩yield方法,放弃了自己的执行权,但是在接下来的线程调度中,仍然还有可能获得cpu的执行权。

public class YieldDemo1 {
    public static void main(String[] args) {
        Thread one = new Thread(() -> {

            for (int i = 0; i < 20; i++) {
                if (i%5 == 0){
                    System.out.println(Thread.currentThread().getName()+"调用yield放弃了cpu执行权");
                    Thread.yield();
                }
            }
            System.out.println(Thread.currentThread().getName()+"结束");
        },"线程1");
        Thread two = new Thread(() -> {
            for (int i = 0; i < 20; i++) {
                if (i%5 == 0){
                    System.out.println(Thread.currentThread().getName()+"调用yield放弃了cpu执行权");
                    Thread.yield();
                }
            }
            System.out.println(Thread.currentThread().getName()+"结束");
        },"线程2");

        one.start();
        two.start();
    }
}

输出结果

线程1调用yield放弃了cpu执行权
线程2调用yield放弃了cpu执行权
线程1调用yield放弃了cpu执行权
线程1调用yield放弃了cpu执行权
线程1调用yield放弃了cpu执行权
线程2调用yield放弃了cpu执行权
线程1结束
线程2调用yield放弃了cpu执行权
线程2调用yield放弃了cpu执行权
线程2结束

可以看看第三、四、五行输出,在线程1放弃了cpu执行权,继续调度的时候,下一次调度还是调度到了线程1.这也验证了我们上面的说法。

  • 总结:yield()从未导致线程转到等待/睡眠/阻塞状态。在大多数情况下,yield()将导致线程从运行状态转到可运行状态。而yield和sleep的区别在于,调用了sleep方法之后,当前调用线程将会被阻塞指定时间,在这期间不会参与调度,而yield只是放弃了自己的时间片执行权,并不会被阻塞,直接以就绪状态去参与下一次的线程调度,下次调度还是有可能会被调度到。

join

在我们平常的编程里,经常会碰到这样的一个场景,例如,我们有A,B,C三种线程,其中A类线程是用于读数据的,然后B类线程是处理数据的,把A类线程读的数据进行汇总,C线程是写入数据的,需要用到A线程读取的数据,并且要等数据全部读取完毕,也就是A线程结束了,B线程才开始处理数据,等B类线程全部处理完毕之后,C类线程才能写入数据。这个场景很常见,多线程加载资源,加载完毕再统一汇总处理。

以上这种场景,如果我们需要自己手动控制判断所有的线程结束了才执行下面的任务,其实是比较麻烦的,因此Thread提供了一个join方法,可以阻塞当前正在运行的线程,让调用join的线程运行完毕之后,当前线程才能继续运行。看代码就明白了

public class join {
    public static void main(String[] args) throws InterruptedException {
        Thread one = new Thread(() ->{
            try {
                System.out.println("线程1");
                Thread.sleep(1000);

                for (;;);

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        Thread two = new Thread(() ->{
            try {
                System.out.println("线程2");
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        one.start();
        two.start();

        one.join();
        two.join();
    }
}

上面这段代码中,首先启动第一个线程,输出线程1 然后休眠,线程2获取执行权,打印线程2 ,之后线程2休眠,线程1获取执行权,然后由于线程1调用了join方法,所以此时主线程阻塞,因为要等线程1执行完毕之后,主线程才会继续执行,但是线程1是个死循环,永远不会结束,所以,这段程序将不会停止,也就是主线程将一直阻塞,two.join也没有执行的机会。

同理,如果A线程中调用B线程的join方法,那么A线程将会被暂时阻塞,直到B线程执行完毕,A线程才会继续,其实对于join,就有点像把多个线程合并成一个单线程来处理

标签:调用,join,Thread,执行权,yield,线程,sleep
From: https://www.cnblogs.com/javaxubo/p/17498745.html

相关文章

  • rt thread pwm使用
    1.rt-threadsettings->组件->设备驱动程序->使用PWM设备驱动程序;2.rt-threadsettings->组件->示例->rtthread设备驱动示例->pwmdevice;3.board.h->#defineBSP_USING_PWM3#defineBSP_USING_PWM3_CH1#defineBSP_USING_PWM3_CH24.stm32f1xx_ha......
  • 三秒看懂Python yield
    业务场景:一个FastAPI构建的网关,数据进来时立即给予一次响应,表示:“收到,我已开始执行。”,然后分发任务另起一个线程异步执行。原本代码写的在异步里又起了一个异步等任务执行,这样不影响下面的return立刻响应。逻辑看起很繁琐,那有没有办法,能让函数在"return":“收到”之后,继续执行呢?......
  • 简单记录下 Spring Boot 使用虚拟线程Virtual Threads(Java的协程)的方法
    在之前的文章中,简单描述了将springboot2.x升级springboot3.1的版本的过程。本文将简单介绍如何在spring中引入虚拟线程,在文章最后会放上一些关于虚拟线程的官方参考资料。JDK22会引来重要特性,VirtualThreads也就是协程功能。与主流的async、await方案(C#、JS等语言)相比......
  • pthread_setschedparam设置实时线程失败
    pthread_setschedparam设置实时线程失败,返回错误码1-Operationnotpermitted,操作不被允许。原因是当前控制台程序没有cgroup中权限两种方式:1、执行如下命令:$$就是当前控制台进程号echo$$>>/sys/fs/cgroup/cpu/tasks2、sysctl-wkernel.sched_rt_runtime_us=-1sysc......
  • ThreadLocal
    关于ThreadLocal介绍ThreadLocal类用来提供线程内部的局部变量。这种变量在多线程环境下访问(通过get和set方法访问)时能保证各个线程的变量相对独立于其他线程内的变量。ThreadLocal实例通常来说都是privatestatic类型的,用于关联线程和线程上下文。作用提供线程内的局部变量,不......
  • RT-THREAD的SFUD驱动简介基于W25Q128
    SFUD简介SFUD是一款开源的串行SPIFlash通用驱动库。详细介绍可查看官方说明,作为一个通用的中间套件,帮用户屏蔽了底层的FLASH操作,也方便用户使用不同的FLASH时进行移植。只需要配置好SPI就可以完成驱动的移植。FLASH特点FLASH写的时候,只能从1写到0,而不能从0写到1。因此写之......
  • STM32:rtthread_schedule调度
    rtthread作为多线程管理的实时操作系统,那么线程与线程之间又是如何切换管理的呢?rtthread中对于多线程切换是通过优先级表搭配优先级组进行调度的,优先级表中存储切换的上下线程节点,优先级组用来判断当前的最高优先级;为了方便理解,在引入优先级表和优先级组之前,需要先了解一下什么......
  • STM32:rtthread_"rt_timer"定时器
    1定时器  轮询系统和前后台系统中的延时为直接阻塞延时,让函数一直等着直到延时够了再继续执行;  大概rtthread觉得直接阻塞延时效率不够高,逻辑不够优美;所以它给每个thread都配置了一个rt_timer类型的thread_timer定时器;  所有定时器由定时器链表统一管理,通过对thread_ti......
  • left/right join中on和where的区别
    开发同学提了个问题,如下两种leftjoin中on和where条件的写法是否等价?select*fromj_aleftjoinj_bonj_a.id=j_b.idwherej_a.name='b';select*fromj_aleftjoinj_bonj_a.id=j_b.idandj_a.name='b';我们先看测试,创建两张测试表,导入一些数据,SQL>createtablej_a(i......
  • OceanBase Join操作
    OceanBase0.4版本中将全面支持SQL,下面给出OceanBase对Join的支持的例子。其中最值得关注的是,OceanBase支持fullJoin哦:)从下面的截图还可以看出来,用的是mysql客户端连接OceanBase。OceanBase完全兼容mysql通信协议。mysql>select*fromj1;+---------+------+|k1|v1......