问题:park和unpark有什么作用?相较于wait和notify有什么优势?
–解答:park和unpark是LockSupport类中提供的两个方法,park()方法可以让正在运行的线程进入等待状态,而unpark()方法可以唤醒等待的线程。
park和unpark不需要获取锁对象后才能使用,每个线程在任何时刻都能调用。相较于notify是随机的唤醒某个线程,unpark可以指定唤醒线程,更加精确
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| package com.deng;
import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest;
import java.util.concurrent.locks.LockSupport;
import static java.util.concurrent.locks.LockSupport.park; import static java.util.concurrent.locks.LockSupport.unpark;
@SpringBootTest @Slf4j public class ParkAndUnparkTest { @Test public void testParkAndUnpark() throws InterruptedException { Thread t1 = new Thread(() -> { log.info("starting..."); log.info("invoke park...."); try { Thread.sleep(2000); } catch (InterruptedException e) { throw new RuntimeException(e); } LockSupport.park(); log.info("starting again..."); },"t1");
t1.start(); log.info("invoke unpark..."); Thread.sleep(1000); LockSupport.unpark(t1); } }
|
1 2 3 4 5
| 2024-03-18T18:20:16.062+08:00 INFO 19268 --- [ main] com.deng.ParkAndUnparkTest : invoke unpark... 2024-03-18T18:20:34.670+08:00 INFO 19268 --- [ t1] com.deng.ParkAndUnparkTest : starting... 2024-03-18T18:20:35.662+08:00 INFO 19268 --- [ t1] com.deng.ParkAndUnparkTest : invoke park.... 2024-03-18T18:21:06.457+08:00 INFO 19268 --- [ t1] com.deng.ParkAndUnparkTest : starting again...
|
问题:为什么unpark可以作用于park之前
–解答:在上述例子中,我们发现一个奇怪的现象:unpark可以在park之前执行,并且park后的线程并没有被阻塞,说明unpark起作用了,这是一个奇怪的现象。要想弄清这一点,我们必须要研究Thread在Java中是如何编写的。
在Java中,每个线程都有自己的一个 Parker 对象,由三部分组成 _counter , _cond 和 _mutex。当调用park方法时,线程会去检查 _counter的值如果为0(默认情况下就是0),那么该线程会获得 _mutex互斥锁进入 _cond等待,并将counter置为0。当调用unpark时,会设置counter为1,并唤醒对应的线程,该线程发现counter为1,于是继续执行。
因此线程调用park进入等待是因为此时线程的counter为0,如果不为0那么调用该方法不会让线程进入等待状态。这也是为什么在park之前调用unpark也会其作用的原因
问题:那是不是可以在park之前调用多次unpark?
–解答:实际情况是,即使你调用了多次unpark,counter的值不会超过1,因此只能抵消一次park