JUC并发编程 -- 线程常用方法之sleep() & yield() & 线程优先级 & sleep方法应用: 限制对 CPU 的使用

Posted Z && Y

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JUC并发编程 -- 线程常用方法之sleep() & yield() & 线程优先级 & sleep方法应用: 限制对 CPU 的使用相关的知识,希望对你有一定的参考价值。

1. sleep() 线程休眠

  1. 调用 sleep 会让当前线程从 Running 进入 Timed Waiting 状态(阻塞)
  2. 其它线程可以使用 interrupt 方法打断正在睡眠的线程,这时 sleep 方法会抛出 InterruptedException
  3. 睡眠结束后的线程未必会立刻得到执行(有可能这时的CPU正在执行其他线程的代码,所以该线程需要等到任务调度器 把新的时间片分给该线程以后,该线程才会继续执行。)
  4. 建议用 TimeUnit 的 sleep 代替 Thread 的 sleep 来获得更好的可读性

1.1 测试sleep()方法改变线程状态

调用 sleep 会让当前线程从 Running 进入 Timed Waiting 状态(阻塞)

测试sleep()方法改变线程状态:

import lombok.extern.slf4j.Slf4j;

@Slf4j(topic = "c.Test6")
public class Test6 {

    public static void main(String[] args) {
        Thread t1 = new Thread("t1") {
            @Override
            public void run() {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };

        t1.start();
        // 因为主线程先启动 所以这时的t1线程是RUNNABLE状态
        log.debug("t1 state: {}", t1.getState());

        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 主线程休眠500ms后 t1线程一定启动了
        log.debug("t1 state: {}", t1.getState());
    }
}

运行结果:


1.2 打断(叫醒)睡眠的线程

其它线程可以使用 interrupt 方法打断正在睡眠的线程,这时 sleep 方法会抛出 InterruptedException

代码:

import lombok.extern.slf4j.Slf4j;

@Slf4j(topic = "c.Test7")
public class Test7 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread("t1") {
            @Override
            public void run() {
                log.debug("enter sleep...");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    log.debug("wake up...");
                    e.printStackTrace();
                }
            }
        };
        t1.start();

        Thread.sleep(1000);
        log.debug("interrupt...");
        t1.interrupt();
    }
}

运行结果:


1.3 TimeUnit 的 sleep

建议用 TimeUnit 的 sleep 代替 Thread 的 sleep 来获得更好的可读性

代码:

@Slf4j(topic = "c.Test8")
public class Test8 {

    public static void main(String[] args) throws InterruptedException {
        log.debug("enter");
        TimeUnit.SECONDS.sleep(2);
        log.debug("end");
    }
}

运行结果:

补充:

其实该方法内部也是调用的Thread的sleep方法,只不过这里做了时间的单位换算


2. yield() 线程礼让

  1. 调用 yield 会让当前线程从 Running 进入 Runnable 就绪状态,然后调度执行其它线程(该线程本来有CPU的使用权,调用该方法后,会让出该方法的CPU使用权,就让其他线程有机会去使用CPU)
  2. 具体的实现依赖于操作系统的任务调度器(这时候可能没有其他的线程在运行,CPU还是会把时间片分给该线程)

3.sleep() vs yield()

  1. 调用sleep方法会让线程进入休眠状态(阻塞状态),CPU会把时间片分给该线程。
  2. 调用yield方法会让线程进入就绪状态,CPU还是有可能把时间片分给该线程。
  3. sleep方法可以加参数(线程休眠的时间),儿yield方法没有参数,调用yield方法只是一瞬间的事情。

4. 线程优先级

  • 线程优先级会提示(hint)调度器优先调度该线程,但它仅仅是一个提示,调度器可以忽略它
  • 如果 cpu 比较忙,那么优先级高的线程会获得更多的时间片,但 cpu 闲时,优先级几乎没作用
  • 线程里面有一个setPriority(int newPriority)方法可以设置线程的优先级

补充:

不管是yield方法还是设置线程优先级,都不能控制线程的调度,最终都是由任务调度器来决定


4.1 测试代码01: 不加以阻止

代码:

import lombok.extern.slf4j.Slf4j;

@Slf4j(topic = "c.Test9")
public class Test9 {

    public static void main(String[] args) {
        Runnable task1 = () -> {
            int count = 0;
            for (; ; ) {
                System.out.println("---->1 " + count++);
            }
        };
        Runnable task2 = () -> {
            int count = 0;
            for (; ; ) {
//                Thread.yield();
                System.out.println("              ---->2 " + count++);
            }
        };
        Thread t1 = new Thread(task1, "t1");
        Thread t2 = new Thread(task2, "t2");
//        t1.setPriority(Thread.MIN_PRIORITY);
//        t2.setPriority(Thread.MAX_PRIORITY);
        t1.start();
        t2.start();
    }
}

运行结果:


4.1 测试代码02: 加上yield方法

代码:

import lombok.extern.slf4j.Slf4j;

@Slf4j(topic = "c.Test9")
public class Test9 {

    public static void main(String[] args) {
        Runnable task1 = () -> {
            int count = 0;
            for (; ; ) {
                System.out.println("---->1 " + count++);
            }
        };
        Runnable task2 = () -> {
            int count = 0;
            for (; ; ) {
                Thread.yield();
                System.out.println("              ---->2 " + count++);
            }
        };
        Thread t1 = new Thread(task1, "t1");
        Thread t2 = new Thread(task2, "t2");
//        t1.setPriority(Thread.MIN_PRIORITY);
//        t2.setPriority(Thread.MAX_PRIORITY);
        t1.start();
        t2.start();
    }
}

运行结果:


4.1 测试代码03: 设置线程优先级

代码:

import lombok.extern.slf4j.Slf4j;

@Slf4j(topic = "c.Test9")
public class Test9 {

    public static void main(String[] args) {
        Runnable task1 = () -> {
            int count = 0;
            for (; ; ) {
                System.out.println("---->1 " + count++);
            }
        };
        Runnable task2 = () -> {
            int count = 0;
            for (; ; ) {
//                Thread.yield();
                System.out.println("              ---->2 " + count++);
            }
        };
        Thread t1 = new Thread(task1, "t1");
        Thread t2 = new Thread(task2, "t2");
        t1.setPriority(Thread.MIN_PRIORITY);// t1线程设置为最小的优先级
        t2.setPriority(Thread.MAX_PRIORITY);// t2线程设置为最大的优先级
        t1.start();
        t2.start();
    }
}

运行结果:


1. sleep方法应用: 限制对 CPU 的使用

  • 在没有利用 cpu 来计算时,不要让 while(true) 空转浪费 cpu,这时可以使用 yield 或 sleep 来让出 cpu 的使用权给其他程序

对应这段代码:

  • 可以用 wait 或 条件变量达到类似的效果
  • 不同的是,后两种都需要加锁,并且需要相应的唤醒操作,一般适用于要进行同步的场景
  • sleep 适用于无需锁同步的场景


以上是关于JUC并发编程 -- 线程常用方法之sleep() & yield() & 线程优先级 & sleep方法应用: 限制对 CPU 的使用的主要内容,如果未能解决你的问题,请参考以下文章

JUC并发编程 -- 应用之统筹(烧水泡茶)

JUC并发编程 -- 线程常用方法之join()详解 & join同步应用 & join限时同步

JUC并发编程 -- 回顾多线程(线程的六种状态 & wait / sleep 的区别)

JUC并发编程 -- 线程常用方法概述 & start() vs run()

JUC并发编程 共享模式之工具 JUC 线程安全的集合类 -- 线程安全的集合类概述

JUC 高并发编程