Skip to content

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()方法唤醒线程。

java
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();
}

使用需要注意:

  1. wait()notify()方法必须要在同步块或者方法里面,且成对出现使用
  2. 调用顺序是先wait后notify才OK

3.2 实现方式2

使用JUC包中Condition的await()方法让线程等待,使用signal()方法唤醒线程。

java
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();
}

使用的注意事项:

  1. Condtion中的线程等待和唤醒方法,需要在外部先获取锁。
  2. 一定要先await后signal,不要反了。

3.3 实现方式3

LockSupport类可以阻塞当前线程以及唤醒指定被阻塞的线程。

java
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()方法:
Alt text 其中UNSAFE类容易造成内存泄漏,不推荐用户直接使用。底层LockSupport调用的Unsafe中的native代码。但是下面代码会有异常,因为LockSupport的permit只有一个:

java
 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()方法时, 如果有凭证,则会直接消耗掉这个凭证然后正常退出:如果无凭证,就必须阻塞等待凭证可用;