前言:
传统的线程等待唤醒机制有两种方式分别是synchronized(wait和notify)和JUC包中的显示锁Lock(condition的await()方法和signal()方法),但是这两个方式有两个缺点,分别是都不能脱离synchronized,和lock、unlock,如果脱离就会报错,还有就是wait和notify,await和signal的执行顺序要固定,必须先wait然后在notify,否则会导致程序无法结束。
所以出现第三种方式,那就是LockSupport(park和unpark),LockSupport类可以阻塞当前线程以及唤醒指定被阻塞的线程。接下来详细介绍。
1、LockSupport介绍
- LockSupport是用来创建锁和其他同步类的基本线程阻塞原语
- LockSupport类使用了一种名为Permit(许可)的概念来做到阻塞和唤醒线程的功能,每个线程都有一个许可(Permit),Permit只有两个值1和0,默认是0。
- 把许可看成是一种(0,1)信号量(Semaphore),但是与Semaphore不同的是,许可的累加上限是1.
- 两个方法
park()/park(Object blocker):阻塞当前线程/阻塞传入的具体线程
unpark(Thread thread):唤醒处于阻塞状态的指定线程 - 调用一次unpark就加1 ,最大就是1
- 调用一次park会消费permit,也就是将1变成0,同时park立即返回。如果再次用park就会阻塞,这时调用unpark会把permit置为1
- 每个线程都有一个相关的permit,permit最多只有一个,重复调用unpark也不会累计凭证
2、代码验证
public static void main(String[] args) {
Thread a = new Thread(() -> {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "\t" + "-----come in " + System.currentTimeMillis());
LockSupport.park(); // 被阻塞....等待通知,他需要通过许可证
System.out.println(Thread.currentThread().getName() + "\t" + "-----被 唤 醒 " + System.currentTimeMillis());
}, "a");
a.start();
Thread b = new Thread(() -> {
LockSupport.unpark(a);
System.out.println(Thread.currentThread().getName() + "\t" + "-----通知了");
}, "b");
b.start();
}
执行结果:
首先 park和unpark 不需要锁块, 其次根据上面的结果我们看出 先unpark(唤醒)后park(等待)也同样支持