线程暂停和唤醒的多种方式

为什么有线程间的通信,线程的通信是理解多线程执行的一个基本知识点,当线程执行到某一步骤时,因为条件不能满足继续执行的需求,需要等待资源满足后才能继续执行。这时线程需要释放它持有的资源让其他线程执行,当相关条件满足后其他线程会通知他继续执行。

线程间通信常常会伴随线程状态的改变,常见的状态改变有:RUNNABLE、BLOCKED、WAITING、TIMED_WAITING,下面总结了一下在java中能够引起以上某些状态改变的方法:

(1)wait / notify、notifyAll

在线程的共享锁上执行wait方法,当前线程会由RUNNABLE状态进入WAITING(没有指定超时时间)或TIMED_WAITING(指定超时时间),这时线程会释放掉它所持有的锁。当线程进入WAITING状态后,必须有其他线程唤醒当前线程,它才能够继续执行,否则当前线程会一直处于 WAITING 状态,而处于 TIMED_WAITING 状态的线程当超时时间到了之后会自动唤醒。唤醒后的线程会重新竞争锁资源,获得锁后进入 RUNNABLE 状态,没有获得锁将进入 BLOCKED 状态。
必须注意一点,wait / notify、notifyAll 代码必须要放在同步代码块中执行,还有在执行 wait 方法后必须要保证能够执行 notify、notifyAll 方法,否则线程有可能会一直休眠。
在Object类中打开查看方法的源代码,这里用蹩脚的中文做了简单翻译:

    /**
     * 唤醒一个正在等待监视器的线程,如果有多个线程正在等待监控器,那么会随机选择一个被唤醒,
     * 唤醒后的线程同其他线程一样需要竞争锁资源,在没有获得锁资源之前,线程会处于阻塞状态。
     * 这个方法只能由作为监控对象的所有者线程调用,线程可以通过三种方式之一成为对象监视器所有者:
     * 1、通过执行该对象的同步实例方法
     * 2、通过执行对象上同步的 synchronized 语句体
     * 3、对于 Class 类型对象,该类的同步静态方法
     * 一次只能有一个线程拥有对象的监视器
     */
    public final native void notify();

    /**
     * 唤醒等待此对象的所有线程,线程通过调用 wait 方法等待对象的监视器。
     * 唤醒后的线程需要竞争锁资源,在没有获得锁之前,唤醒后的线程无法继续执行,
     * 同 notify 方法一样,只能由此对象监视器所有者的线程调用
     */
    public final native void notifyAll();

    /**
     * 使当前线程等待另一个线程调用 notify() 方法或 notifyAll() 方法唤醒,或者指定时间已过自动唤醒。
     * 当前线程必须拥有此对象的监视器才能调用 wait 方法,所以该方法必须放在同步代码块中执行,
     * 线程调用方法后将自身置于此对象的等待集中,然后放弃对象上的所有同步声明并处于休眠状态,直到以下四种情况之一发生:
     * 1、其他线程调用此对象的 notify 方法,而当前线程恰好被选中为唤醒线程
     * 2、其他线程调用此对象的 notifyAll 方法
     * 3、其他线程调用 interrupt() 方法中断线程运行
     * 4、指定超时时间已过
     * 唤醒后的线程从该对象的等待集中移除,并重新启用线程调度,在竞争到锁后恢复到调用 wait 方法时的状态继续运行。
     * 线程也可以在不被通知、中断或超时的情况下唤醒,即所谓的虚假唤醒。虽然这种情况很少发生但是在开发中必须防范,
     * 所以等待应该总是在循环体中发生:
     *     synchronized (obj) {
     *         while (条件不成立)
     *             obj.wait(timeout);
     *     }
     * 
     * wait 方法只会解锁当前对象,当前线程等待时,当前线程的其他对象仍然保持锁定。
     * 同 notify 方法一样,只能由此对象监视器所有者的线程调用
     */
    public final native void wait(long timeout) throws InterruptedException;

    /**
     * 与 wait(long timeout) 方法类似,只是多了一个纳秒为单位的超时时间,所以超时的时间还需要加上 nanos 纳秒。
     */
    public final void wait(long timeout, int nanos) throws InterruptedException {
        if (timeout < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (nanos < 0 || nanos > 999999) {
            throw new IllegalArgumentException(
                                "nanosecond timeout value out of range");
        }

        if (nanos > 0) {
            timeout++;
        }

        wait(timeout);
    }

    /**
     * 没有指定超时时间,调用该方法的线程只能被其他线程通过 notify 或 notifyAll 方法唤醒,否则会一直等待
     */
    public final void wait() throws InterruptedException {
        wait(0);
    }

通过上面的源代码可以看出,方法最终调用的是用 native 修饰的本地方法,在源代码中也指出了有关方法使用的更多信息,参考Doug Lea的《Concurrent Programming in Java》或Joshua Bloch的《Effective Java Programming Language Guide》里面的介绍。

下面示例一个 wait 的用法:启动多个线程,每个线程给定一个编号,当编号相同时,就执行打印任务,其他线程则等待,理想状态下输出顺序应该是 0 -> 1 -> 2 … 0 -> 1 -> 2:

import java.util.ArrayList;
import java.util.List;

/**
 * 主线程
 * @author xingo
 * @date 2021/2/20
 */
public class TestPrintNumber {

    public static final List<Thread> threadList = new ArrayList<Thread>();

    public static void main(String[] args) throws InterruptedException {
        int total = 3;

        Number testNum = new Number(total);
        for(int i = 0; i < total; ++i) {
            PrintWorker printWorker = new PrintWorker(i, testNum);
            Thread thread = new Thread(printWorker, "PrintWorker-" + i);
            threadList.add(thread);
            thread.start();
        }

        for(Thread thread : threadList) {
            thread.join();
        }

        System.out.println("--------------------------------------------");
        System.out.println("主线程 " + Thread.currentThread().getName() + " 输出所有的线程状态");
        for(Thread thread : TestPrintNumber.threadList) {
            System.out.println(thread.getName() + " : " + thread.getState());
        }
    }
}

/**
 * 打印线程,当对象中的值与线程编号相等时,该线程打印数值并将下一个打印值+1
 * @author xingo
 * @date 2021/2/18
 */
class PrintWorker implements Runnable {

    private int threadNum;
    private Number testNum;

    public PrintWorker(int threadNum, Number testNum) {
        this.threadNum = threadNum;
        this.testNum = testNum;
    }

    @Override
    public void run() {
        while (this.testNum.getNum() < 100) {
            synchronized (this.testNum) {
                System.out.println("---------------- " + Thread.currentThread().getName() + " 进入线程执行 ----------------");
                // threadNum 与 printThread 相等,就将对象中的num值+1并打印出该值,同时通知下一个线程执行打印任务
                if(this.threadNum == this.testNum.getPrintThread()) {
                    this.testNum.setNum(this.testNum.getNum() + 1);
                    System.out.println(Thread.currentThread().getName() + " ===>>> " + this.testNum.getNum());
                    int noticeThread = (this.threadNum + 1) % this.testNum.getTotalThread();
                    this.testNum.setPrintThread(noticeThread);
                    this.testNum.notify();

                    System.out.println("在线程 " + Thread.currentThread().getName() + " if中输出线程状态");
                    for(Thread thread : TestPrintNumber.threadList) {
                        System.out.println(thread.getName() + " : " + thread.getState());
                    }
                } else {    //如果threadNum 与 printThread 不等,就通知其他线程执行,同时该线程释放占用的资源
                    try {
                        this.testNum.notify();  //
                        this.testNum.wait();

                        System.out.println("在线程 " + Thread.currentThread().getName() + " else中输出线程状态");
                        for(Thread thread : TestPrintNumber.threadList) {
                            System.out.println(thread.getName() + " : " + thread.getState());
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

/**
 * 数值类
 * @author xingo
 * @date 2021/2/20
 */
class Number {

    private int printThread = 0;    //当前正在执行输出的线程序号
    private int totalThread;        //运行线程总数
    private int num;                //计数值

    public Number(int totalThread) {
        this.totalThread = totalThread;
    }
    public int getPrintThread() {
        return printThread;
    }
    public void setPrintThread(int printThread) {
        this.printThread = printThread;
    }
    public int getNum() {
        return num;
    }
    public void setNum(int num) {
        this.num = num;
    }
    public int getTotalThread() {
        return totalThread;
    }
}

上面的代码执行后的一次输出结果为:

(1)---------------- PrintWorker-0 进入线程执行 ----------------
PrintWorker-0 ===>>> 1
在线程 PrintWorker-0 if中输出线程状态
PrintWorker-0 : RUNNABLE
PrintWorker-1 : BLOCKED
PrintWorker-2 : BLOCKED
(2)---------------- PrintWorker-0 进入线程执行 ----------------
(3)---------------- PrintWorker-2 进入线程执行 ----------------
(4)---------------- PrintWorker-1 进入线程执行 ----------------
PrintWorker-1 ===>>> 2
在线程 PrintWorker-1 if中输出线程状态
PrintWorker-0 : BLOCKED
PrintWorker-1 : RUNNABLE
PrintWorker-2 : BLOCKED
(5)---------------- PrintWorker-1 进入线程执行 ----------------
(6)在线程 PrintWorker-2 else中输出线程状态
PrintWorker-0 : BLOCKED
PrintWorker-1 : WAITING
PrintWorker-2 : RUNNABLE
(7)---------------- PrintWorker-2 进入线程执行 ----------------
PrintWorker-2 ===>>> 3
在线程 PrintWorker-2 if中输出线程状态
PrintWorker-0 : BLOCKED
PrintWorker-1 : BLOCKED
PrintWorker-2 : RUNNABLE
(8)---------------- PrintWorker-2 进入线程执行 ----------------
(9)在线程 PrintWorker-0 else中输出线程状态
PrintWorker-0 : RUNNABLE
PrintWorker-1 : BLOCKED
PrintWorker-2 : WAITING
(10)---------------- PrintWorker-0 进入线程执行 ----------------
PrintWorker-0 ===>>> 4
在线程 PrintWorker-0 if中输出线程状态
PrintWorker-0 : RUNNABLE
PrintWorker-1 : BLOCKED
PrintWorker-2 : BLOCKED

........

---------------- PrintWorker-2 进入线程执行 ----------------
PrintWorker-2 ===>>> 99
在线程 PrintWorker-2 if中输出线程状态
PrintWorker-0 : BLOCKED
PrintWorker-1 : BLOCKED
PrintWorker-2 : RUNNABLE
---------------- PrintWorker-2 进入线程执行 ----------------
在线程 PrintWorker-1 else中输出线程状态
PrintWorker-0 : BLOCKED
PrintWorker-1 : RUNNABLE
PrintWorker-2 : WAITING
---------------- PrintWorker-1 进入线程执行 ----------------
在线程 PrintWorker-0 else中输出线程状态
PrintWorker-0 : RUNNABLE
PrintWorker-1 : WAITING
PrintWorker-2 : BLOCKED
---------------- PrintWorker-0 进入线程执行 ----------------
PrintWorker-0 ===>>> 100
在线程 PrintWorker-0 if中输出线程状态
PrintWorker-0 : RUNNABLE
PrintWorker-1 : BLOCKED
PrintWorker-2 : BLOCKED
在线程 PrintWorker-2 else中输出线程状态
PrintWorker-0 : RUNNABLE
PrintWorker-1 : BLOCKED
PrintWorker-2 : RUNNABLE
在线程 PrintWorker-1 else中输出线程状态
PrintWorker-0 : TERMINATED
PrintWorker-1 : RUNNABLE
PrintWorker-2 : TERMINATED
--------------------------------------------
主线程 main 输出所有的线程状态
PrintWorker-0 : TERMINATED
PrintWorker-1 : TERMINATED
PrintWorker-2 : TERMINATED

Process finished with exit code 0

现在分析一下执行结果:
(1)首先序号为0的线程先执行,执行完后通知其他线程执行。
(2)由于所有线程都是RUNNABLE状态,执行完的线程0又抢到资源进入方法,但此时线程0不满足条件执行了else方法:首先通知其他处于 WAITING 状态的线程,然后执行 wait() 方法,由于此时没有线程处于 WAITING 状态,这时所有线程去争夺锁资源,获得锁资源的线程将进入同步方法执行。
(3)线程2得到了锁资源进入同步方法执行,但由于线程2不满足if条件就走了else分支也进入了 WAITING 状态。
(4)线程1抢到资源进入同步方法执行了if方法,执行完成后通知其他线程执行,从打印结果可以看出,除了当前线程其他两个线程都处于 BLOCKED 状态,本来处于 WAITING 状态的线程0 被线程2的 notify() 方法唤醒,处于 WAITING 状态的线程2被线程1的 notify() 方法唤醒。
(5)线程1抢到锁资源进入方法执行,但是不满足if条件执行了else方法进入 WAITING 状态。
(6)处于 WAITING 状态的线程2继续执行else方法的剩余代码,输出所有的线程状态。
(7)执行完后的线程2又获得锁资源继续进入同步方法执行,这时的线程2满足if条件进入分支运行。
(8)线程2又抢到资源进入同步方法,但是此时线程2也不满足if条件执行了else分支进入 WAITING 状态。
(9)线程0被唤醒继续执行 else 方法。
(10)线程0抢到资源进入同步方法执行if分支 …
end: 所有线程在以上的步骤中循环往复的运行,直到线程到达TERMINATED状态。
分析上面的代码可知,线程的运行状态是无序的,也是不可预测的。

(2)sleep

线程的sleep方法定义在Thread类中,打开Thread类可以看到sleep方法的定义如下:

    /**
     * 使当前执行的线程休眠指定的毫秒数,线程在休眠时不会失去监视器的所有权
     */
    public static native void sleep(long millis) throws InterruptedException;

    /**
     * 使当前执行的线程休眠指定的毫秒数加上纳秒数,线程在休眠时不会失去监视器的所有权
     */
    public static void sleep(long millis, int nanos) throws InterruptedException {
        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (nanos < 0 || nanos > 999999) {
            throw new IllegalArgumentException(
                                "nanosecond timeout value out of range");
        }

        if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
            millis++;
        }

        sleep(millis);
    }

可见sleep方法也是用 native 关键字修饰的,表示这是本地方法,也就是需要操作系统帮忙实现,java语言只管调用就可以了。

sleep方法经常用于和wait方法比较使用,他们都能够让当前线程停止运行,但是他们又有一些不同,大致总结有以下几个方面:

1、wait 是对象方法,而 sleep 是类方法。
2、wait 使用时必须在同步代码块中,而 sleep 不需要。
3、wait 方法进入 WAITING 状态后会释放对象的锁,而 sleep 不会释放锁资源。
4、wait 进入 WAITING 状态后的能够被 notify 或 notifyAll 唤醒,而 sleep 不能被其他线程唤醒,只能等到超时时间后自动执行。
5、wait 常常用于线程间的通信,而 sleep 更多的用于让当前线程停下来一会儿,停完后继续运行。

对比两者的不同,其他几点都比较容易理解,我们主要验证一下第3点,比较一下两个方法暂停运行后对锁资源的处理方式:
两个方法都在同步代码块中分别休眠一段时间输出内容:
sleep方法

import java.util.ArrayList;
import java.util.List;

/**
 * @author xingo
 * @date 2021/2/25
 */
public class TestSleep {

    public static final List<Thread> list = new ArrayList<Thread>();

    public static void main(String[] args) {
        for(int i = 0; i < 2; ++i) {
            Thread thread = new Thread(new SleepThread(), "thread" + i);
            list.add(thread);
            thread.start();
        }
    }
}

class SleepThread implements Runnable {

    @Override
    public void run() {

        synchronized (TestSleep.class) {
            System.out.println("===>>> " + Thread.currentThread().getName() + " 休眠前输出线程状态 :");
            for(Thread thread : TestSleep.list) {
                System.out.println(thread.getName() + " 线程当前状态 : " + thread.getState());
            }

            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println("===>>> " + Thread.currentThread().getName() + " 休眠后输出线程状态 :");
            for(Thread thread : TestSleep.list) {
                System.out.println(thread.getName() + " 线程当前状态 : " + thread.getState());
            }
        }
    }
}

输出内容:
===>>> thread0 休眠前输出线程状态 :
thread0 线程当前状态 : RUNNABLE
thread1 线程当前状态 : RUNNABLE
===>>> thread0 休眠后输出线程状态 :
thread0 线程当前状态 : RUNNABLE
thread1 线程当前状态 : BLOCKED
===>>> thread1 休眠前输出线程状态 :
thread0 线程当前状态 : RUNNABLE
thread1 线程当前状态 : RUNNABLE
===>>> thread1 休眠后输出线程状态 :
thread0 线程当前状态 : TERMINATED
thread1 线程当前状态 : RUNNABLE

Process finished with exit code 0

wait方法

import java.util.ArrayList;
import java.util.List;

/**
 * @author xingo
 * @date 2021/2/25
 */
public class TestWait {

    public static final List<Thread> list = new ArrayList<Thread>();

    public static void main(String[] args) {
        for(int i = 0; i < 2; ++i) {
            Thread thread = new Thread(new WaitThread(), "thread" + i);
            list.add(thread);
            thread.start();
        }
    }
}

class WaitThread implements Runnable {

    @Override
    public void run() {

        synchronized (TestWait.class) {
            System.out.println("===>>> " + Thread.currentThread().getName() + " 休眠前输出线程状态 :");
            for(Thread thread : TestWait.list) {
                System.out.println(thread.getName() + " 线程当前状态 : " + thread.getState());
            }

            try {
                TestWait.class.wait(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println("===>>> " + Thread.currentThread().getName() + " 休眠后输出线程状态 :");
            for(Thread thread : TestWait.list) {
                System.out.println(thread.getName() + " 线程当前状态 : " + thread.getState());
            }
        }
    }
}

输出内容:
===>>> thread0 休眠前输出线程状态 :
thread0 线程当前状态 : RUNNABLE
thread1 线程当前状态 : RUNNABLE
===>>> thread1 休眠前输出线程状态 :
thread0 线程当前状态 : TIMED_WAITING
thread1 线程当前状态 : RUNNABLE
===>>> thread0 休眠后输出线程状态 :
thread0 线程当前状态 : RUNNABLE
thread1 线程当前状态 : TIMED_WAITING
===>>> thread1 休眠后输出线程状态 :
thread0 线程当前状态 : RUNNABLE
thread1 线程当前状态 : RUNNABLE

Process finished with exit code 0

从结果输出可知,在调用sleep方法后其他线程并不能进入同步方法执行,也就是锁资源没有释放;而wait方法执行后其他线程能够进入同步方法。这也验证了两个方法对锁资源的处理方式。

(3)park / unpark

这两个方法定义在LockSupport类中,用于对线程进行阻塞和解除阻塞的作用,与wait/notify方法不用的是,他们不需要放入同步代码块中执行,也不需要保证调用顺序的先后。当unpark在park之前调用,再次调用park方法时当前线程也不会被阻塞。这是因为:
1、unpark调用时,如果当前线程还未进入park,则许可为true。
2、park调用时,判断许可是否为true:如果是true,则继续往下执行;如果是false,则等待,直到许可为true。
在LockSupport类中有多个park/unpark的重载方法,功能相似,现在只选择两个做一下注释:

    /**
     *
     * 如果许可可用,该方法会立即返回;否则当前线程将进入休眠状态,直到发生以下情况之一会继续运行:
     * 其他线程以当前线程为目标调用了unpark()方法;
     * 其他线程中断了当前线程;
     * 线程发生错误
     *
     * 此方法不报告导致方法返回的原因,所以在park()方法继续运行后,调用者应该首先重新检查当前线程的中断状态
     */
    public static void park() {
        UNSAFE.park(false, 0L);
    }

    /**
     * 使给定线程的许可证可用:
     * 如果线程在park()上被阻塞,那么它将解除阻塞;否则下一次调用park()保证不会阻塞。
     * 如果给定的线程尚未启动,则不能保证此操作有任何效果(也就是再次调用park()方法后仍可能会阻塞)。
     */
    public static void unpark(Thread thread) {
        if (thread != null)
            UNSAFE.unpark(thread);
    }

使用时有一点需要注意,就是park方法后要判断线程状态,下面示例一个使用

import java.util.concurrent.locks.LockSupport;

/**
 * @author xingo
 * @date 2021/3/23
 */
public class ParkAndUnpark {

    public static void main(String[] args) throws Exception {
        Thread t1 = new Thread(new ParkThread(), "thread-01");
        t1.start();

        Thread.sleep(20);
        LockSupport.unpark(t1);

        System.out.println(Thread.currentThread().getName() + " 线程中执行了unpark方法");
    }
}

class ParkThread implements Runnable {

    @Override
    public void run() {
        try {
            System.out.println(Thread.currentThread().getName() + " 线程中输出内容");

            Thread.sleep(500);
            LockSupport.park();
            if (Thread.currentThread().isInterrupted()) {
                System.out.println(Thread.currentThread().getName() + " 线程被中断了");
            }

            System.out.println(Thread.currentThread().getName() + " 线程中执行了park方法后继续运行");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

输出内容:

thread-01 线程中输出内容
main 线程中执行了unpark方法
thread-01 线程中执行了park方法后继续运行

Process finished with exit code 0

这段代码模拟了先执行unpark方法后再执行park方法,可见线程并没有被阻塞,如果线程先执行了park方法,那么就需要其他线程对当前线程执行unpark方法才能让当前线程继续运行。这区别于wait/notify,调用了wait方法的线程必须被其他线程notify后才能再次运行,并且一定要保证wait后有其他线程执行notify方法,如果notify方法在wait方法之前执行,那么该线程也不能被唤醒。

(4)suspend / resume / stop

这三个方法都是定义在Thread类中,由于已经被标记为Deprecated,也就不必过多研究。他们也是能够改变线程的状态,但是由于会引发死锁等相关问题,所以不建议使用:
被废弃的方法

(5)join

join方法定义在Thread线程中,它是让当前线程等待其他线程执行完成后才能继续执行,在其他线程执行过程中,当前线程将被阻塞。
在Thread方法中join方法的定义:

    /**
     * 等待某个线程执行完成,并指定超时时间
     */
    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) {
            while (isAlive()) {  //由于是通过子线程对象调用的join()方法,所以这里判断的是子线程是否存活,如果存活主线程就等待
                wait(0);  //这里等待的是主线程,当子线程执行完成后会调用notifyAll方法唤醒所有线程。所以不建议在子线程上面使用wait、notify或notifyAll相关的方法
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }

    /**
     * 在等待时间上在加一个纳秒时间
     */
    public final synchronized void join(long millis, int nanos)
    throws InterruptedException {

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

        if (nanos < 0 || nanos > 999999) {
            throw new IllegalArgumentException(
                                "nanosecond timeout value out of range");
        }

        if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
            millis++;
        }

        join(millis);
    }

    /**
     * 等待线程执行完成
     */
    public final void join() throws InterruptedException {
        join(0);
    }

这个方法很简单,举个例子就明白了

/**
 * @author xingo
 * @date 2021/3/23
 */
public class JoinTest {

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new JoinThread(), "thread-01");
        t1.start();
        t1.join();

        System.out.println(Thread.currentThread().getName() + " 线程中输出内容");
    }
}

class JoinThread implements Runnable {

    @Override
    public void run() {
        try {
            System.out.println(Thread.currentThread().getName() + " 线程中输出内容");

            Thread.sleep(2000);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

输出内容:

thread-01 线程中输出内容
main 线程中输出内容

Process finished with exit code 0

正常情况下,如果main方法中没有调用子线程的join方法,那么主线程的输出会早于子线程的输出,join方法后主线程会等待子线程执行完成后再继续运行。这里需要注意一点,join方法要在子线程执行了start()方法后再调用,否则阻塞将不起作用。

(6)yeild

yeild是定义在Thread类中的静态方法,他并不能让当前进行进入阻塞状态,只是让当前线程重新进入就绪状态去抢占CPU的调度权,这个时候它并不会释放获取到的锁资源,也不接受中断。在平时开发中使用的场景比较少,如果一定要用它的话,就是在那种复杂的任务执行过程中,担心当前线程长时间占用CPU,可以调用yeild让出CPU的调度权,等下次获取到再继续执行,这样不但能完成自己的任务,也能给其他线程一些运行的机会。
在Thread中方法的定义如下:

    /**
     * 表示当前线程放弃对处理器的使用,也就是释放CPU的时间片,使当前线程重新变成就绪状态,并重新竞争CPU的调度权,
     * 这样当前线程有可能获取到CPU也有可能获取不到CPU
     */
    public static native void yield();

由于方法简单没啥使用场景,也就不举例了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值