前言
sleep()、wait()、park()都可以使线程进入等待状态,但是3种方式在使用上和功能上都有些不同。
一、sleep()
1、sleep()方法是Thread类的方法,可以在任何线程中使用,使用时需要传入时间参数,如Thread.sleep(1000),睡眠1秒。
2、如果sleep()方法写在同步代码块中,线程并不会释放锁,其它线程没法获取锁。
二、wait & notify()、notifyAll()
1、wait()是Object类中的方法,因为每个对象都拥有锁,所以让当前线程等待某个对象的锁,当然应该通过这个对象来操作。调用wait()方法线程会释放锁,并进入Monitor中的等待队列中。
2、这三个方法都是本地方法,并且被final修饰,无法被重写,并且只有采用synchronized实现线程同步时才能使用这三个方法。
3、wait()有个有参的方法,可以传入时间参数,当等待一定时间后自动唤醒。
4、唤醒线程需要其它线程调用notify()或者notifyAll(),调用notify()会随机唤醒一个等待中的线程,notifyAll()会将所有等待队列中的线程唤醒,唤醒后的线程开始竞争锁,竞争锁失败的线程进入就绪队列。
注意:
1、 每个锁对象都有两个队列,一个是就绪队列,一个是阻塞队列。就绪队列存储了已就绪(将要竞争锁)的线程,阻塞队列存储了被阻塞的线程。当一个阻塞线程被唤醒后,才会进入就绪队列,进而等待CPU的调度。反之,当一个线程被wait后,就会进入阻塞队列,等待被唤醒。
2、wait()方法只能写在 synchronized代码块中,因为只有synchronized的重量级锁才有阻塞队列。如果wait()写在synchronized代码块外,会报异常IllegalMonitorStateException。
三、park、unpark
1、park 是 LockSupport类的方法,通过LockSupport.park()方式调用。park()方法不会释放锁,会将当前线程标记为休眠,直到其它线程调用unpark,并将阻塞的线程作为参数,可以精准唤醒线程。
2、当调用unpark时,如果要唤醒的线程没有在休眠,就将线程中的_counter字段标记为1,如果该线程调用park方法时,如果_counter字段为1,就将字段标记为0,不会进入休眠。也就是说park和unpark顺序可以颠倒。
3、park方法可以写在同步代码块外。