1. Object类中的wait和notify方法实现线程等待和唤醒
private static void waitNotify() { Object obj = new Object(); new Thread(() -> { try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { } synchronized (obj) { try { System.out.println(Thread.currentThread().getName() + "启动了"); obj.wait(); System.out.println(Thread.currentThread().getName()+"被唤醒"); } catch (InterruptedException e) { } } }, "线程一").start(); new Thread(() -> { synchronized (obj) { System.out.println(Thread.currentThread().getName() + "启动了"); obj.notify(); System.out.println(Thread.currentThread().getName()+"开始唤醒其它线程"); } }, "线程二").start(); }
限制:
wait和notify方法必须要在同步块或者方法里面且成对出现使用,否则会抛出java.lang.IllegalMonitorStateException。
调用顺序要先wait后notify才OK。
2. Condition接口中的await后signal方法实现线程的等待和唤醒,与Object类中的wait和notify方法实现线程等待和唤醒类似。
private static void awaitSignal() { ReentrantLock lock = new ReentrantLock(); Condition condition = lock.newCondition(); new Thread(()->{ try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { } lock.lock(); System.out.println(Thread.currentThread().getName() + "启动了"); try { condition.await(); } catch (InterruptedException e) { } System.out.println(Thread.currentThread().getName()+"被唤醒了"); lock.unlock(); },"t1").start(); new Thread(()->{ lock.lock(); System.out.println(Thread.currentThread().getName() + "启动了"); condition.signal(); System.out.println(Thread.currentThread().getName()+"开始唤醒其它线程"); lock.unlock(); },"t2").start(); }
await和signal方法必须要在同步块或者方法里面且成对出现使用,否则会抛出java.lang.IllegalMonitorStateException。
调用顺序要先await后signal才OK。
3. LockSupport类中的park等待和unpark唤醒
public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(() -> { System.out.println(Thread.currentThread().getName() + "启动了"); LockSupport.park(); System.out.println(Thread.currentThread().getName() + "被唤醒了"); }, "t1"); t1.start(); Thread t2 = new Thread(() -> { System.out.println(Thread.currentThread().getName() + "启动了"); try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { } LockSupport.unpark(t1); System.out.println(Thread.currentThread().getName() + "开始唤醒其它指定线程"); }, "t2"); t2.start(); }
LockSupport类使用了一种名为Permit(许可)的概念来做到阻塞和唤醒线程的功能,每个线程都有一个许可(permit),permit只有两个值1和零,默认是零。可以把许可看成是一种(0.1)信号量(Semaphore),但与Semaphore不同的是,许可的累加上限是1。permit默认是0,所以一开始调用park()方法,当前线程就会阻塞,直到别的线程将当前线程的permit设置为1时,park方法会被唤醒,然后会将permit再次设置为0并返回。调用unpark(thread)方法后,就会将thread线程的许可permit设置成1(注意多次调用unpark方法,不会累加,pemit值还是1)会自动唤醒thead线程,即之前阻塞中的LockSupport.park()方法会立即返回。
优势:不用加锁,而且可以等待和唤醒的顺序可以任意。