LockSupport
1. 简介
LockSupport是用来创建锁和其他同步类的基本线程阻塞原语。它是一个线程阻塞的工具类。可以让线程在任意位置阻塞,阻塞之后也有对应的唤醒方法。
2. 唤醒等待API
2.1 park方法
permit许可证默认没有不能放行,所以一开始调park()
方法当前线程就会阻塞,直到别的线程给当前线程的发放permit,park方法才会被唤醒。
2.2 unpark方法
调用unpark(thread)方法后,就会将thread线程的许可证permit发放,会自动唤醒park线程, 即之前阻塞中的LockSupport.park()
方法会立即返回。
3. 唤醒等待实操
3.1 实现方式1
使用Object中的wait()
方法让线程笑待,使用Object中的notify()
方法唤醒线程。
public static void main(String[] args) {
Object objLock = new Object();
Thread t1 = new Thread(() -> {
System.out.println("开始执行。。。");
synchronized (objLock){
try {
objLock.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("结束执行。。。");
}
}, "t1");
t1.start();
Thread t2 = new Thread(() -> {
synchronized (objLock){
objLock.notify();
System.out.println("唤醒线程:"+t1.getName());
}
}, "t2");
t2.start();
}
使用需要注意:
wait()
和notify()
方法必须要在同步块或者方法里面,且成对出现使用- 调用顺序是先wait后notify才OK
3.2 实现方式2
使用JUC包中Condition的await()
方法让线程等待,使用signal()
方法唤醒线程。
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();
Thread t1 = new Thread(() -> {
System.out.println("开始执行。。。");
try {
lock.lock();
condition.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
System.out.println("结束执行。。。");
}, "t1");
t1.start();
Thread t2 = new Thread(() -> {
try {
lock.lock();
System.out.println("唤醒线程:"+t1.getName());
condition.signal();
}finally {
lock.unlock();
}
}, "t2");
t2.start();
}
使用的注意事项:
- Condtion中的线程等待和唤醒方法,需要在外部先获取锁。
- 一定要先await后signal,不要反了。
3.3 实现方式3
LockSupport类可以阻塞当前线程以及唤醒指定被阻塞的线程。
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
System.out.println("开始执行。。。");
LockSupport.park();
System.out.println("结束执行。。。");
}, "t1");
t1.start();
Thread t2 = new Thread(() -> {
LockSupport.unpark(t1);
System.out.println("解锁t1...");
}, "t2");
t2.start();
}
使用LockSupport无需在外部使用锁,唤醒和等待的顺序不再要求,只需要注意阻塞和唤醒要成对出现即可。
4. 底层原理
LockSupport类使用了一种名为Permit(许可)的概念来做到阻塞和唤醒线程的功能,每个线程都有一个许可(permit),但与Semaphore不同的是,许可的累加上限是1。其中park()
方法底层调用Unsafe类的park()
方法: 其中UNSAFE类容易造成内存泄漏,不推荐用户直接使用。底层LockSupport调用的Unsafe中的native代码。但是下面代码会有异常,因为LockSupport的permit只有一个:
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
System.out.println("开始执行。。。");
LockSupport.park();
LockSupport.park();
System.out.println("结束执行。。。");
}, "t1");
t1.start();
Thread t2 = new Thread(() -> {
LockSupport.unpark(t1);
LockSupport.unpark(t1);
System.out.println("解锁t1...");
}, "t2");
t2.start();
}
LockSupport.unpark(t1)
无论执行多少次都只获取1个通行证。当调用park()
方法时, 如果有凭证,则会直接消耗掉这个凭证然后正常退出:如果无凭证,就必须阻塞等待凭证可用;