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

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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值