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_Louis 和 Consumer_Jimmy 都被同时唤醒。
可以将 lock.notifyAll(); 替换为 lock.notify(); 重新运行程序,将会看见系统每次都将在Consumer_Louis 和 Consumer_Jimmy 之间随机选择一个线程来唤醒。