多线程sleep、yield、wait、join方法的使用和区别

使用和介绍

sleep方法

sleep方法的官方注释(翻译后):

    /**
     * 根据系统计时器和调度程序的精度和准确性,使当前执行的线程休眠(暂时停止执行)指定的毫秒数。线程不会失去任何监视器的所有权(不释放锁)。
     *
     * @param millis 以毫秒为单位的睡眠时间长度
     * @throws IllegalArgumentException 如果参数{millis}的值为负数
     * @throws InterruptedException     如果有线程中断了当前线程。当抛出此异常时,当前线程的中断状态将被清除。
     */
    public static native void sleep(long millis) throws InterruptedException;

sleep方法是Thread类的静态方法,而且是‘native’方法所以一般情况下是看不到具体实现代码的,不过通过注释也可以简单了解它的使用方法:使当前执行的线程休眠(暂时停止执行)指定的毫秒数。

所谓多线程实际上是CPU的快速切换调用!sleep()方法既然让当前线程休眠了,所以必然要释放CPU处于阻塞状态,然后等sleep的时间结束后再次变为可运行状态等待CPU的调用。

yield方法

 yield方法的官方注释(翻译后):

    /**
     * 向调度程序提示当前线程愿意放弃其对处理器的当前使用。调度程序可以忽略此提示。
     * <p>
     * Yield是一种启发式尝试,旨在改善线程之间的相对进度,否则线程将过度使用CPU。它的使用应该与详细的分析和基准测试相结合,以确保它确实具有预期的效果。
     * <p>
     * 使用这种方法很少是合适的。它可能有助于调试或测试目的,有助于重现由于竞争条件而产生的错误。在设计并发控制结构(如{java.util.concurrent.locks}包中的结构)时,它也可能很有用。
     */
    public static native void yield();

yield方法也是Thread类的静态方法,也是‘native’方法。根据注释分析得出三点:

1、‘放弃其对处理器的当前使用’意思是该方法可以让当前线程让出CPU资源;

2、‘调度程序可以忽略此提示’说明线程并没有被阻塞,而是可以随时被调用,也就是处于可运行状态;

3、既然yield()方法可以让出CPU而且也可能被忽视即让出后立即获取资源则说明只有具有相同优先级或者更高优先级的线程才可以与yield()让出的线程竞争CPU的调度,也就是涉及到线程的优先级。

wait方法

  wait方法的官方注释(翻译后):

/**
     * 让当前线程处于等待状态,直到另一个线程调用{notify}或者{notifyAll}方法。
     * 换句话来说,这个方法和 wait(0)是一样的。
     * <p>
     * 当前线程必须拥有此对象的监视器(锁).线程释放这个监视器(锁)的拥有权,并且直到另一个线程通过调用{notify}或者{notifyAll}方法通知在该对象的监视器上等待唤醒的线程
     * 然后线程处于等待状态,直到它能够重新获得监视器的所有权并恢复执行。
     */
    public final void wait() throws InterruptedException {
        wait(0);
    }

    public final native void wait(long timeout) throws InterruptedException;

wait()方法是Object类的方法,而且wait()方法实际上是调用wait(long timeout)方法,而wait(long timeout)方法也是native方法。根据注释可知:wait方法首先要和锁一起使用,而且wait会使当前线程释放CPU处于阻塞状态的同时,也会释放锁,然后等另一个线程通过notify()或者notifyAll()方法来唤醒它,并且在被唤醒之后才处于可运行状态。

notify方法:

    /**
     * 唤醒在该对象的监视器上等待的单个线程。如果有任何线程正在等待此对象,则会选择其中一个线程进行唤醒。
     * 这种选择是任意的,由执行人员自行决定。线程通过调用其中一个{wait}方法来等待对象的监视器。
     */
    public final native void notify();

notifyAll方法:

    /**
     * 唤醒在该对象的监视器上等待的所有线程。线程通过调用其中一个{wait}方法来等待对象的监视器。
     */
    public final native void notifyAll();

join方法

 join方法的官方注释(翻译后):

/**
     * 一直等到调用线程执行完毕(死掉).
     *
     * @throws InterruptedException 如果有线程中断了当前线程。当抛出此异常时,当前线程的<i>中断状态</i>将被清除。
     */
    public final void join() throws InterruptedException {
        join(0);
    }

    public final synchronized void join(long millis)
            throws InterruptedException {
        long base = System.currentTimeMillis();
        long now = 0;

        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (millis == 0) {
            //当线程还在运行状态时调用wait方法
            while (isAlive()) {
                wait(0);
            }
        } else {
            //当线程还在运行状态时调用wait方法
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }

join方法是Thread类中的一个方法,该方法的定义是阻塞当前正在运行的线程A,开始执行调用此方法的线程B直到线程B执行完毕。

问题1:现有T1、T2、T3三个线程,如何保证这三个线程能按T1、T2、T3的顺序执行?

答案:将T1线程join到T2线程的run方法中,将T2线程join到T3线程的run方法中。

public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("t1");

            }
        });
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    t1.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("t2");
            }
        });
        Thread t3 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    t2.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("t3");
            }
        });
        t1.start();
        t2.start();
        t3.start();
    }

问题2:join方法是如何阻塞当前线程的?

答案:join方法实际上是用了synchronized锁,并调用wait方法。并且在JVM源码中封装了notifyAll方法从而在调用线程执行结束后唤醒当前线程。参考:https://blog.csdn.net/u010983881/article/details/80257703

区别

 sleep()和wait()

相同点:

都可以让当前线程让出CPU资源而且是处于阻塞状态。

不同点:

1、sleep()是Thread类的方法,wait()是Object类的方法;

2、sleep()需要指定参数,wait()不需要参数;

3、sleep()到时间后自动处于可运行状态,wait()方法需要另一个线程通过notify()或者notifyAll()方法将其唤醒;

4、sleep()方法的调用线程不需要拥有锁资源,wait()方法的线程在有锁资源的情况下调用。

sleep()和yield()

相同点:

1、都可以让当前线程让出CPU资源;

2、都是Thread类的静态方法;

不同点:

1、sleep()需要指定参数。yield()方法不需要参数;

2、sleep()在指定时间内线程处于阻塞状态等时间结束后才处于可运行状态,yield()让出资源的线程直接处于可运行状态;

3、sleep()处于可运行状态时不涉及线程优先级,和其他线程公平竞争资源,而yield()是为了让同级别或者更高优先级的线程先获取资源。

wait()和yield()

相同点:

1、都可以让当前线程让出CPU资源;

2、都可以不指定参数;

不同点:

1、yield()是Thread类的方法,wait()是Object类的方法;

2、yield()让出资源后自动处于可运行状态,wait()方法需要另一个线程通过notify()或者notifyAll()方法将其唤醒;

3、yield()方法的调用线程不需要拥有锁资源,wait()方法的线程在有锁资源的情况下调用。

posted @ 2024-01-27 10:12  请别耽误我写BUG  阅读(215)  评论(0编辑  收藏  举报