sleep和wait到底什么区别
其实这个问题应该这么问——sleep和wait有什么相同点。因为这两个方法除了都能让当前线程暂停执行完,几乎没有其它相同点。
wait方法是Object类的方法,这意味着所有的Java类都可以调用该方法。
sleep方法是Thread类的静态方法。
wait
wait是在当前线程持有wait对象锁的情况下,暂时放弃锁,并让出CPU资源,并积极等待其它线程调用同一对象的notify或者notifyAll方法。注意,即使只有一个线程在等待,并且有其它线程调用了notify或者notifyAll方法,等待的线程只是被激活,但是它必须得再次获得锁才能继续往下执行。换言之,即使notify被调用,但只要锁没有被释放,原等待线程因为未获得锁仍然无法继续执行。测试代码如下所示
import java.util.Date; public class Wait { public static void main(String[] args) { Thread thread1 = new Thread(() -> { synchronized (Wait.class) { try { System.out.println(new Date() + " Thread1 is running"); Wait.class.wait(); System.out.println(new Date() + " Thread1 ended"); } catch (Exception ex) { ex.printStackTrace(); } } }); thread1.start(); Thread thread2 = new Thread(() -> { synchronized (Wait.class) { try { System.out.println(new Date() + " Thread2 is running"); Wait.class.notify(); // Don't use sleep method to avoid confusing for(long i = 0; i < 200000; i++) { for(long j = 0; j < 100000; j++) {} } System.out.println(new Date() + " Thread2 release lock"); } catch (Exception ex) { ex.printStackTrace(); } } for(long i = 0; i < 200000; i++) { for(long j = 0; j < 100000; j++) {} } System.out.println(new Date() + " Thread2 ended"); }); // Don't use sleep method to avoid confusing for(long i = 0; i < 200000; i++) { for(long j = 0; j < 100000; j++) {} } thread2.start(); } }
执行结果如下
Tue Jun 14 22:51:11 CST 2016 Thread1 is running Tue Jun 14 22:51:23 CST 2016 Thread2 is running Tue Jun 14 22:51:36 CST 2016 Thread2 release lock Tue Jun 14 22:51:36 CST 2016 Thread1 ended Tue Jun 14 22:51:49 CST 2016 Thread2 ended 从运行结果可以看出
- thread1执行wait后,暂停执行
- thread2执行notify后,thread1并没有继续执行,因为此时thread2尚未释放锁,thread1因为得不到锁而不能继续执行
- thread2执行完synchronized语句块后释放锁,thread1得到通知并获得锁,进而继续执行
注意:wait方法需要释放锁,前提条件是它已经持有锁。所以wait和notify(或者notifyAll)方法都必须被包裹在synchronized语句块中,并且synchronized后锁的对象应该与调用wait方法的对象一样。否则抛出IllegalMonitorStateException
sleep
sleep方法告诉操作系统至少指定时间内不需为线程调度器为该线程分配执行时间片,并不释放锁(如果当前已经持有锁)。实际上,调用sleep方法时并不要求持有任何锁。
package com.test.thread; import java.util.Date; public class Sleep { public static void main(String[] args) { Thread thread1 = new Thread(() -> { synchronized (Sleep.class) { try { System.out.println(new Date() + " Thread1 is running"); Thread.sleep(2000); System.out.println(new Date() + " Thread1 ended"); } catch (Exception ex) { ex.printStackTrace(); } } }); thread1.start(); Thread thread2 = new Thread(() -> { synchronized (Sleep.class) { try { System.out.println(new Date() + " Thread2 is running"); Thread.sleep(2000); System.out.println(new Date() + " Thread2 ended"); } catch (Exception ex) { ex.printStackTrace(); } } for(long i = 0; i < 200000; i++) { for(long j = 0; j < 100000; j++) {} } }); // Don't use sleep method to avoid confusing for(long i = 0; i < 200000; i++) { for(long j = 0; j < 100000; j++) {} } thread2.start(); } }
执行结果如下
Thu Jun 16 19:46:06 CST 2016 Thread1 is running Thu Jun 16 19:46:08 CST 2016 Thread1 ended Thu Jun 16 19:46:13 CST 2016 Thread2 is running Thu Jun 16 19:46:15 CST 2016 Thread2 ended
由于thread 1和thread 2的run方法实现都在同步块中,无论哪个线程先拿到锁,执行sleep时并不释放锁,因此其它线程无法执行。直到前面的线程sleep结束并退出同步块(释放锁),另一个线程才得到锁并执行。
注意:sleep方法并不需要持有任何形式的锁,也就不需要包裹在synchronized中。
本文所有示例均基于Java HotSpot(TM) 64-Bit Server VM
调用sleep方法的线程,在jstack中显示的状态为sleeping
。
java.lang.Thread.State: TIMED_WAITING (sleeping)
调用wait方法的线程,在jstack中显示的状态为on object monitor
java.lang.Thread.State: WAITING (on object monitor)