问题: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