Java中的sleep() | wait() | notify() | notifyAll()

来源:互联网 发布:淘宝王子团队怎么联系 编辑:程序博客网 时间:2024/05/22 02:23

sleep()方法

首先,看 源代码Javadoc

/** * Causes the currently executing thread to sleep (temporarily cease * execution) for the specified number of milliseconds, subject to * the precision and accuracy of system timers and schedulers. The thread * does not lose ownership of any monitors. * * @param  millis *         the length of time to sleep in milliseconds * * @throws  IllegalArgumentException *          if the value of {@code millis} is negative * * @throws  InterruptedException *          if any thread has interrupted the current thread. The *          <i>interrupted status</i> of the current thread is *          cleared when this exception is thrown. */public static native void sleep(long millis) throws InterruptedException;

可以看出这是调用的本地方法,所以在这里看不到具体的细节了,所以我们就来看一下Javadoc怎么说吧。

首先看第一句:

Causes the currently executing thread to sleep (temporarily cease execution) for the specified number of milliseconds将导致当前线程在指定的毫秒时间段沉睡,并暂时释放CPU时间片

而后面又提到:

The thread does not lose ownership of any monitors线程将不会释放任何已获得的锁 (这里的monitor指的就是我们平时所说的锁)

可以看出,sleep()方法只会使线程沉睡一段时间,但是如果线程在这之前已经获得了锁,那么这些锁将不会因为sleep()方法的调用而被释放。

参考以下场景:

Object object = new Object;synchronized (object) {    try {        Thread.sleep(1000);    } catch (InterruptedException e) {        // do something...    }}

这时,对象object作为被当前线程获得,调用sleep()方法并不会释放

再来看对于异常信息的描述:

@throws  InterruptedException    if any thread has interrupted the current thread. The interrupted status of the     current thread is cleared when this exception is thrown.如果任何其它线程在当前线程執行sleep()方法时中断了当前线程,则会抛出InterruptedException(中断异常),并且当前线程的‘中断状态’将在异常抛出之后被清除(复位)。

可以看出,sleep()方法在执行时是可以被其它线程中断的,所以在调用sleep()方法时,需要对InterruptedException做出必要的处理来响应中断状态。


wait() notify() 和 notifyAll()

上面介绍了sleep()方法,现在再来看wait() notify() 和 notifyAll()方法。

wait() notify()notifyAll() 是Object类中的final类型方法,不可以被子类Override。由于Object类是所有类的父类,所以所有类和它们的实例都将拥有这三个方法。

public final native void wait(long timeout) throws InterruptedException;public final void wait() throws InterruptedException {        wait(0);}public final native void notify();public final native void notifyAll();

可以看出它们也都是本地方法,查看Javadoc,将这三个方法总结一下:

wait() notify()和notifyAll()的使用有个共同的前提条件,就是当前线程必须获得 这三个方法所属实例 的锁(monitor)。也就是说,它们的调用必须在同步块的内部,不然就会抛出 IllegalMonitorStateException

参考下面的代码:

Object object = new Object();synchronized (object) {    try {        object.wait();    } catch (InterruptedException e) {        // do something...    }    // object.notify();    object.notifyAll();}


而如下这样的调用则是错误的,将会抛出 IllegalMonitorStateException,因为当前线程并没有获得objectB的锁(monitor):

Object objectA = new Object();Object objectB = new Object();synchronized (objectA) {    try {        objectB.wait();    } catch (InterruptedException e) {        // do something...    }    // object.notify();    objectB.notifyAll();}

当一个线程调用了wait()或wait(0),它将进入WAITING状态,如果调用wait(timeout)则将进入TIMED_WAITING状态。

当一个线程进入了某个锁(monitor)的wait()方法后,将释放这个锁,并且线程退出wait()方法有以下三种情况:
1. 中断
2. 对于当前调用wait()方法的锁(monitor)对象objectA,其它线程获得了这个锁objectA,并且调用objectA的notify()或notifyAll()方法
3. 超时(当且仅当当前状态是TIMED_WAITING,如果是WAITING,则将等待直到条件1或2出现,否则将成为死锁

对于某个锁(monitor)对象的notify()或notifyAll()方法,它们都是用于通知持有此锁并调用了wait()方法的线程,从WAITING状态唤醒。

notify()方法是在等待队列的所有线程中随机选择一个进行通知,而notifyAll()则是通知所有等待的线程。

下面用文字描述一个例子:

对于某个对象lock,当一个线程A获得lock的锁(monitor),并调用lock.wait(),线程B随后获得lock的锁(monitor),也调用lock.wait()。此时,线程A和B都处于WAITING状态,并且lock的锁已经被释放。线程C随后获得lock的锁(monitor),并调用lock.notify(),并在退出lock的同步块之前做了某操作M。随后,系统将在A和B中随机选择一个出来,并将其唤醒。如果线程C获得锁之后调用了lock.notifyAll(),那么随后线程A和B将同时被唤醒。这里值得注意的是,当线程C调用了notify()或notifyAll()后,线程A或B不会立刻被唤醒,而是等待线程C的后续操作M完毕,并退出lock的同步块,释放了lock的锁(monitor)之后,线程A或B才能重新获得lock的锁(monitor),并被唤醒。



举例子

为了让后面的代码尽量的简洁,先写出几个公共方法:

/** * 线程休眠,并对中断异常{@link InterruptedException}仅作控制台输出处理 * * @param millis 休眠时长(毫秒) */public static void sleepIgnoreInterrupt(long millis) {    try {        Thread.sleep(millis);    } catch (InterruptedException e) {        e.printStackTrace();    }}/** * 为一个指定的线程创建一个监控线程,尽力按顺序采集线程所经历的每一个{@link Thread.State} * * @param thread 被监控的线程 */public static void startThreadStateDetector(Thread thread) {    Thread stateDetector = new Thread(new Runnable() {            @Override            public void run() {                LinkedHashSet<String> states = new LinkedHashSet<>();                while (thread.getState() != Thread.State.TERMINATED)                    states.add(thread.getState().name());                states.add(thread.getState().name());                System.out.println(String.format("Thread-[%s], States %s", thread.getName(), states));    );    stateDetector.start();    // 等待监控线程完全启动    sleepIgnoreInterrupt(300);}/** * 打印带有线程名称的日志 * * @param message 日志内容 */public static void printMsgWithThreadName(String message) {    System.out.println("Thread-[" + Thread.currentThread().getName() + "] " + message);}


1. 首先举一个关于wait()和notify()的简单例子:

public static void printSimpleWaitAndNotify() {    Object lock = new Object();    Thread threadJimmy = new Thread(new Runnable() {        @Override        public void run() {            synchronized (lock) {                try {                    printMsgWithThreadName("Ready to invoke wait()...");                    lock.wait();                    printMsgWithThreadName("Exit wait()...");                } catch (InterruptedException e) {                    // do something...                }            }        }    }, "ThreadJimmy");    Thread threadLouis = new Thread(new Runnable() {        @Override        public void run() {            synchronized (lock) {                printMsgWithThreadName("Ready to invoke notify()...");                lock.notify();                printMsgWithThreadName("Exit notify()...");            }        }    }, "ThreadLouis");    // 监控线程的状态变化    startThreadStateDetector(threadJimmy);    startThreadStateDetector(threadLouis);    threadJimmy.start();    // 等待线程threadJimmy调用wait()方法并释放锁    sleepIgnoreInterrupt(1000);    threadLouis.start();}

运行结果为:

Thread-[ThreadJimmy] Ready to invoke wait()...Thread-[ThreadLouis] Ready to invoke notify()...Thread-[ThreadLouis] Exit notify()...Thread-[ThreadJimmy] Exit wait()...Thread-[ThreadJimmy], States [NEW, RUNNABLE, WAITING, BLOCKED, TERMINATED]Thread-[ThreadLouis], States [NEW, RUNNABLE, TERMINATED]

从运行结果可以清晰的看到,ThreadJimmy获得lock的锁(monitor)并调用wait()方法,随后释放了lock的锁(monitor),随后ThreadLouis获得lock的锁(monitor)并调用notify()方法,释放lock的锁(monitor),随后ThreadJimmy重新获得lock的锁(monitor),并退出wait()方法。

从线程的States监控日志中可以看出,ThreadJimmy在WAITING状态之后进入了BLOCKED状态,表明ThreadLouis在调用notify()之后,释放lock的锁(monitor)之前的这段时间里ThreadJimmy是被阻塞了的。

2. 再来个稍微复杂一点的例子:

private static void printComplexWaitAndNotify() {    Object lock = new Object();    Thread consumerLouis = new Thread(new Runnable() {        @Override        public void run() {            while (!Thread.currentThread().isInterrupted()) {                synchronized (lock) {                    try {                        printMsgWithThreadName("Wait for notify...");                        lock.wait();                        printMsgWithThreadName("notify received...\n");                    } catch (InterruptedException e) {                        printMsgWithThreadName("Interrupted!");                        return;                    }                }                try {                    Thread.sleep(1000);                } catch (InterruptedException e) {                    printMsgWithThreadName("Interrupted!");                    return;                }            }            printMsgWithThreadName("Interrupted!");        }    }, "Consumer_Louis");    Thread consumerJimmy = new Thread(new Runnable() {        @Override        public void run() {            while (!Thread.currentThread().isInterrupted()) {                synchronized (lock) {                    try {                        printMsgWithThreadName("Wait for notify...");                        lock.wait();                        printMsgWithThreadName("notify received...\n");                    } catch (InterruptedException e) {                        printMsgWithThreadName("Interrupted!");                        return;                    }                }                try {                    Thread.sleep(1000);                } catch (InterruptedException e) {                    printMsgWithThreadName("Interrupted!");                    return;                }            }            printMsgWithThreadName("Interrupted!");        }    }, "Consumer_Jimmy");    Thread notifier = new Thread(new Runnable() {        @Override        public void run() {            while (!Thread.currentThread().isInterrupted()) {                synchronized (lock) {                    printMsgWithThreadName("Begin Notifying...");                    lock.notifyAll();                    printMsgWithThreadName("End Notifying...");                }                try {                    Thread.sleep(1000);                } catch (InterruptedException e) {                    printMsgWithThreadName("Interrupted!");                    return;                }            }            printMsgWithThreadName("Interrupted!");        }    }, "notifier");    startThreadStateDetector(consumerLouis);    startThreadStateDetector(consumerJimmy);    startThreadStateDetector(notifier);    consumerLouis.start();    consumerJimmy.start();    notifier.start();    sleepIgnoreInterrupt(5000);    consumerLouis.interrupt();    consumerJimmy.interrupt();    notifier.interrupt();}

运行结果为:

Thread-[Consumer_Louis] Wait for notify...Thread-[notifier] Begin Notifying...Thread-[notifier] End Notifying...Thread-[Consumer_Jimmy] Wait for notify...Thread-[Consumer_Louis] notify received...Thread-[notifier] Begin Notifying...Thread-[notifier] End Notifying...Thread-[Consumer_Jimmy] notify received...Thread-[Consumer_Louis] Wait for notify...Thread-[Consumer_Jimmy] Wait for notify...Thread-[notifier] Begin Notifying...Thread-[notifier] End Notifying...Thread-[Consumer_Jimmy] notify received...Thread-[Consumer_Louis] notify received...Thread-[notifier] Begin Notifying...Thread-[notifier] End Notifying...Thread-[Consumer_Jimmy] Wait for notify...Thread-[Consumer_Louis] Wait for notify...Thread-[notifier] Begin Notifying...Thread-[notifier] End Notifying...Thread-[Consumer_Louis] notify received...Thread-[Consumer_Jimmy] notify received...Thread-[Consumer_Louis] Interrupted!Thread-[notifier] Interrupted!Thread-[Consumer_Jimmy] Interrupted!Thread-[Consumer_Louis], States [NEW, RUNNABLE, WAITING, BLOCKED, TIMED_WAITING, TERMINATED]Thread-[notifier], States [NEW, RUNNABLE, BLOCKED, TIMED_WAITING, TERMINATED]Thread-[Consumer_Jimmy], States [NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED]

可以看到每次在notifier调用了notifyAll()之后,Consumer_LouisConsumer_Jimmy 都被同时唤醒。

可以将 lock.notifyAll(); 替换为 lock.notify(); 重新运行程序,将会看见系统每次都将在Consumer_LouisConsumer_Jimmy 之间随机选择一个线程来唤醒。

0 0