LockSupport是用来创建锁和其他同步类的基本线程阻塞原语。
LockSupport中park()和unpark()方法的作用分别是阻塞线程和解除阻塞线程。
线程等待唤醒机制(wait/notify)
3种让线程等待和唤醒的方法
- 使用Object中的wait()方法让线程等待,使用Object中的notify()方法唤醒线程。
- 使用JUC包中的Condition的await()方法让线程等待,使用signal()方法唤醒线程。
- LockSupport类可以阻塞当前线程已经唤醒其指定被阻塞的线程。
使用Object中的wait和notify方法需要注意:
- Object类的wait、notify、notifyAll用于线程等待和唤醒的方法,都必须在synchronized内部执行,必须用到关键字synchronized。
- 先wait后notify、notifyAll方法,等待中的线程才会被唤醒,否则无法唤醒。
传统的synchronized和Lock实现等待唤醒通知的约束
- 线程先要获得并持有锁,必须在锁块(synchronized和Lock)中
- 必须要先等待后唤醒,线程才能够被唤醒
LockSupport类中的park等待和unpark唤醒
通过park()和unpark(thread)方法来实现阻塞和唤醒线程的操作。
LockSupport类使用了一种名为permit(许可)的概念来做到阻塞和唤醒线程的功能,每个线程都有一个许可(Permit)。
Permit只有两个值1和0,默认为0。可以把许可看成是一种(0,1)信号量(Semaphore),但与Semaphore不同的是,许可的累加上限是1。
public static void park(Object blocker); // 暂停当前线程 public static void parkNanos(Object blocker, long nanos); // 暂停当前线程,不过有超时时间的限制 public static void parkUntil(Object blocker, long deadline); // 暂停当前线程,直到某个时间 public static void park(); // 无期限暂停当前线程 public static void parkNanos(long nanos); // 暂停当前线程,不过有超时时间的限制 public static void parkUntil(long deadline); // 暂停当前线程,直到某个时间 public static void unpark(Thread thread); // 恢复当前线程 public static Object getBlocker(Thread t);
阻塞:
park()/park(Object blocker)
阻塞当前线程/阻塞传入的具体线程
//调用LockSupport.park时的底层方法 public static void park() { UNSAFE.park(false, 0L); }
permit默认是0,所以一开始调用park()方法,当前线程就会阻塞,直到别的线程将当前线程的permit设置为1时,park方法才会被唤醒,然后会将permit再次设置为0并返回。
唤醒:
unpark(Thread thread)
唤醒处于阻塞状态的指定线程
//调用LockSupport.unpark(thread)的底层方法 public static void unpark(Thread thread) { if (thread != null) UNSAFE.unpark(thread); }
调用unpark(thread)方法后,就会将thread线程的许可permit设置为1(注意多次调用unpark方法,不会累加,permit还是为1)会自动唤醒thread线程,即直线阻塞中的LockSupport.park()方法会立即返回。
public class LockSupportDemo { public static void main(String[] args) { Thread t1 = new Thread(() -> { System.out.println(Thread.currentThread().getName()+"\t"+"调用"); LockSupport.park(); System.out.println(Thread.currentThread().getName()+"\t"+"二次调用"); }, "t1"); t1.start(); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } Thread t2 = new Thread(() -> { System.out.println(Thread.currentThread().getName()+"\t"+"调用"); LockSupport.unpark(t1); System.out.println(Thread.currentThread().getName()+"\t"+"二次调用"); }, "t2"); t2.start(); } }
结果:

线程阻塞需要消耗凭证(permit),这个凭证最多只有一个
当调用park方法时,如果有凭证,则会直接消耗掉这个凭证然后正常退出;如果无凭证,就必须阻塞线程等待凭证可用。
而unpark方法则相反,它会增加一个凭证,但凭证最多只能有一个,无法累加。
posted on
浙公网安备 33010602011771号