前言: 本文中的代码基于JDK1.8
LockSupport是什么?
LockSupport定义了一组公共的静态方法,这些方法提供了最基本的线程阻塞和唤醒功能,LockSupport是成为构建同步工具的基础工具。
LockSupport定义了一组以park开头的方法来阻塞当前线程,以及unpark(Thread thread)方法来唤醒一个被阻塞的线程。Park有停车的意思,假设线程为车辆,那么park方法代表着停车,而unpark方法则是指车辆启动离开。
体验一下LockSupport的用法
park
@Test
void testPark() throws InterruptedException {
Thread testThread = new Thread(() -> {
log.info("start...");
LockSupport.park(this);
log.info("end...");
}, "testThread");
testThread.start();
TimeUnit.SECONDS.sleep(3);
log.info("unpark testThread");
LockSupport.unpark(testThread);
}
2021-11-25 18:52:26.191 [testThread] INFO c.e.t.c.locksupport.LockSupportTest [] - start...
2021-11-25 18:52:29.190 [main] INFO c.e.t.c.locksupport.LockSupportTest [] - unpark testThread
2021-11-25 18:52:29.192 [testThread] INFO c.e.t.c.locksupport.LockSupportTest [] - end...
从输出结果看,testThread线程启动后进入了休眠状态,直到3秒后被唤醒
parkNanos
@Test
void testParkNanos() throws InterruptedException {
Thread testThread = new Thread(() -> {
log.info("start...");
LockSupport.parkNanos(this, TimeUnit.SECONDS.toNanos(1));
log.info("end...");
}, "testThread");
testThread.start();
TimeUnit.SECONDS.sleep(3);
log.info("unpark testThread");
}
2021-11-25 18:53:03.820 [testThread] INFO c.e.t.c.locksupport.LockSupportTest [] - start...
2021-11-25 18:53:04.828 [testThread] INFO c.e.t.c.locksupport.LockSupportTest [] - end...
2021-11-25 18:53:06.822 [main] INFO c.e.t.c.locksupport.LockSupportTest [] - unpark testThread
可以看到testThread线程休眠1秒后就自动唤醒了
parkUntil
@Test
void testParkUntil() {
long time = System.currentTimeMillis();
log.info("开始休眠:{}", time);
LockSupport.parkUntil(this, time + 2000);
log.info("休眠结束");
}
2021-11-25 18:53:25.815 [main] INFO c.e.t.c.locksupport.LockSupportTest [] - 开始休眠:1637837605813
2021-11-25 18:53:27.817 [main] INFO c.e.t.c.locksupport.LockSupportTest [] - 休眠结束
线程休眠了2秒
核心方法
public static void park()
方法描述:
- 如果permit可用,则消耗掉这个permit,并立即结束方法
- 如果permit不可用,则进入休眠状态
唤醒条件:
- 调用这个线程的unpark方法
- 调用线程的interrupt方法
- 错误的没有原因的返回
可以看到park实际是调用了UNSAFE类的park方法,这是一个native方法,底层是由C++实现的。
public static void park() {
UNSAFE.park(false, 0L);
}
public static void park(Object blocker)
方法描述:
- 如果permit可用,则消耗掉这个permit,并立即结束方法
- 如果permit不可用,则进入休眠状态
唤醒条件:
- 调用这个线程的unpark方法
- 调用线程的interrupt方法
- 错误的没有原因的返回
至于这个blocker到底是干什么的,我们后面再分析
public static void park(Object blocker) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(false, 0L);
setBlocker(t, null);
}
public static void parkNanos(Object blocker, long nanos)
方法描述:
- 如果permit可用,则消耗掉这个permit,并立即结束方法
- 如果permit不可用,则进入休眠状态
唤醒条件:
- 调用这个线程的unpark方法
- 调用线程的interrupt方法
- 经过了指定的时间
- 错误的没有原因的返回
public static void parkNanos(Object blocker, long nanos) {
if (nanos > 0) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(false, nanos);
setBlocker(t, null);
}
}
public static void parkUntil(Object blocker, long deadline)
方法描述:
- 如果permit可用,则消耗掉这个permit,并立即结束方法
- 如果permit不可用,则进入休眠状态
唤醒条件:
- 调用这个线程的unpark方法
- 调用线程的interrupt方法
- 指定的时间已过
- 错误的没有原因的返回
parkNanos(Object blocker, long nanos) 与 parkUntil(Object blocker, long deadline)的区别是
parkNanos指定的是具体休眠多少纳秒,而parkUntil执行的是什么时间醒过来,是等待到的绝对时间(以毫秒为单位)
// 注意这里的deadline时间单位是毫秒
public static void parkUntil(Object blocker, long deadline) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(true, deadline);
setBlocker(t, null);
}
blocker的作用
在Java6中,LockSupport新增了park(Object blocker)、parkNanos(Object blocker, long nanos)和parkUntil(Object blocker, long deadline)三个方法,其中参数blocker是用来标识当前线程在等待的对象,该对象主要用于排查和系统监控。
这是由于在Java5之前,当线程阻塞(使用synchronized关键字)在一个对象上时,通过线程dump能够查看到该线程的阻塞对象,方便定位,而Java5新推出的Lock等并发工具时缺遗漏了这一点,导致在线程dump时无法提供阻塞对象的信息,因此,在Java6中,LockSupport新增了上述3个含有阻塞对象的park方法,用来代替原来的park方法。
下面我们实现一个简单的锁来看看有没有blocker的区别
首先我们定义一个接口Lock,定义锁的方法
public interface Lock {
/**
* 加锁
*/
void lock();
/**
* 解锁
*/
void unlock();
}
再定义一个抽象队列同步器
public abstract class SimpleAbstractQueuedSynchronizer {
/**
* 等待队列头部node
*/
private transient volatile Node head;
/**
* 等待队列的尾部node
*/
private transient volatile Node tail;
/**
* 同步状态
*/
private volatile int state;
/**
* 持有同步状态的线程
*/
private transient Thread exclusiveOwnerThread;
/**
* 设置同步器的同步状态
* @param newState 同步状态
*/
protected final void setState(int newState) {
state = newState;
}
/**
* 获取同步器的同步状态
* @return 同步状态
*/
protected final int getState() {
return state;
}
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
protected final boolean compareAndSetState(int expect, int update) {
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
protected boolean tryRelease(int arg) {
throw new UnsupportedOperationException();
}
protected final void setExclusiveOwnerThread(Thread thread) {
exclusiveOwnerThread = thread;
}
protected final Thread getExclusiveOwnerThread() {
return exclusiveOwnerThread;
}
static void selfInterrupt() {
Thread.currentThread().interrupt();
}
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(), arg))
selfInterrupt();
}
private void setHead(Node node) {
head = node;
node.thread = null;
node.prev = null;
}
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null;
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
private boolean parkAndCheckInterrupt() {
LockSupport.park();
return Thread.interrupted();
}
private void cancelAcquire(Node node) {
if (node == null)
return;
node.thread = null;
Node pred = node.prev;
while (pred.waitStatus > 0)
node.prev = pred = pred.prev;
Node predNext = pred.next;
node.waitStatus = Node.CANCELLED;
if (node == tail && compareAndSetTail(node, pred)) {
compareAndSetNext(pred, predNext, null);
} else {
int ws;
if (pred != head &&
((ws = pred.waitStatus) == Node.SIGNAL ||
(ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
pred.thread != null) {
Node next = node.next;
if (next != null && next.waitStatus <= 0)
compareAndSetNext(pred, predNext, next);
} else {
unparkSuccessor(node);
}
node.next = node;
}
}
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
LockSupport.unpark(s.thread);
}
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
return true;
if (ws > 0) {
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
private Node addWaiter() {
Node node = new Node(Thread.currentThread());
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) {
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
private boolean compareAndSetHead(Node update) {
return unsafe.compareAndSwapObject(this, headOffset, null, update);
}
private boolean compareAndSetTail(Node expect, Node update) {
return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
}
private static boolean compareAndSetWaitStatus(Node node, int expect, int update) {
return unsafe.compareAndSwapInt(node, waitStatusOffset, expect, update);
}
private static boolean compareAndSetNext(Node node, Node expect, Node update) {
return unsafe.compareAndSwapObject(node, nextOffset, expect, update);
}
static final class Node {
static final int CANCELLED = 1;
static final int SIGNAL = -1;
volatile int waitStatus;
volatile Node prev;
volatile Node next;
volatile Thread thread;
Node predecessor() throws NullPointerException {
Node p = prev;
if (p == null)
throw new NullPointerException();
else
return p;
}
Node() { }
Node(Thread thread) {
this.thread = thread;
}
}
private static final Unsafe unsafe;
private static final long stateOffset;
private static final long headOffset;
private static final long tailOffset;
private static final long waitStatusOffset;
private static final long nextOffset;
static {
try {
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
unsafe = (Unsafe) f.get(null);
stateOffset = unsafe.objectFieldOffset
(SimpleAbstractQueuedSynchronizer.class.getDeclaredField("state"));
headOffset = unsafe.objectFieldOffset
(SimpleAbstractQueuedSynchronizer.class.getDeclaredField("head"));
tailOffset = unsafe.objectFieldOffset
(SimpleAbstractQueuedSynchronizer.class.getDeclaredField("tail"));
waitStatusOffset = unsafe.objectFieldOffset
(Node.class.getDeclaredField("waitStatus"));
nextOffset = unsafe.objectFieldOffset
(Node.class.getDeclaredField("next"));
} catch (Exception ex) { throw new Error(ex); }
}
}
定义一个锁的实现类
public class SimpleLock implements Lock {
private static class Sync extends SimpleAbstractQueuedSynchronizer {
/**
* 尝试加锁
* @param acquires 状态值
* @return {@code true} 加锁成功
*/
@Override
protected boolean tryAcquire(int acquires) {
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
@Override
protected boolean tryRelease(int release) {
int c = getState() - release;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
}
private final Sync sync = new Sync();
/**
* 加锁
*/
@Override
public void lock() {
sync.acquire(1);
}
/**
* 解锁
*/
@Override
public void unlock() {
sync.release(1);
}
}
好了,一把简单地锁,就完成了,下面我们来测试一下
@Test
void testLock() throws InterruptedException {
Lock lock = new SimpleLock();
Runnable runnable = () -> {
lock.lock();
try {
log.info("lock success");
TimeUnit.SECONDS.sleep(30);
log.info("end task");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
};
Thread thread1 = new Thread(runnable, "thread1");
Thread thread2 = new Thread(runnable, "thread2");
Thread thread3 = new Thread(runnable, "thread3");
thread1.start();
thread2.start();
thread3.start();
thread1.join();
thread2.join();
thread3.join();
}
执行jstack命令查看线程状态
可以看到thread2和thread3正处于WAITING状态,但是却不能和synchronized一样,提示出正在争用哪把锁。
下面我们试试加了blocker之后的样子
修改SimpleAbstractQueuedSynchronizer类的parkAndCheckInterrupt方法
private boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
再运行一下上面的测试方法,可以看到已经可以和synchronized一样,显示出正在争用的锁对象了。
park唤醒以后为什么要自己再做检查?
可以看到LockSupport中注释说明了,park可能会被没有原因的唤醒。并且park方法不会报告导致方法返回的原因。调用方应首先重新检查导致线程停止的条件。
JUC里面几乎所有的等待操作都是使用了LockSupport来实现的,并且里面也都会在park唤醒后,重新做检查,防止被错误的唤醒,感兴趣的同学可以看一下JUC中是怎样使用LockSupport的。
当对LockSupport执行中断会怎么样?
interface Task {
void task() throws Exception;
}
private Thread createThread(String threadName, Task task) {
return new Thread(() -> {
try {
log.info("start...");
task.task();
log.info("end...");
} catch (Exception e) {
log.error(e.getMessage());
}
}, threadName);
}
@Test
void testInterrupted() throws InterruptedException {
Thread thread1 = createThread("thread1", () -> Thread.sleep(1000 * 60 * 10));
Thread thread2 = createThread("thread2", () -> LockSupport.park(this));
thread1.start();
thread2.start();
TimeUnit.SECONDS.sleep(3);
thread1.interrupt();
thread2.interrupt();
log.info("thread1 interrupt:{}", thread1.isInterrupted());
log.info("thread2 interrupt:{}", thread2.isInterrupted());
}
输出结果为:
2021-11-25 21:35:24.679 [thread1] INFO c.e.t.c.locksupport.LockSupportTest [] - start...
2021-11-25 21:35:24.679 [thread2] INFO c.e.t.c.locksupport.LockSupportTest [] - start...
2021-11-25 21:35:27.682 [thread1] ERROR c.e.t.c.locksupport.LockSupportTest [] - sleep interrupted
2021-11-25 21:35:27.682 [thread2] INFO c.e.t.c.locksupport.LockSupportTest [] - end...
2021-11-25 21:35:27.681 [main] INFO c.e.t.c.locksupport.LockSupportTest [] - thread1 interrupt:true
2021-11-25 21:35:27.687 [main] INFO c.e.t.c.locksupport.LockSupportTest [] - thread2 interrupt:false
可以发现中断对sleep和LockSupport都会生效,都会打断休眠状态,但是LockSupport中断后不会重置中断状态,依然是true,并且不会抛出异常,而sleep会抛出异常,并且重置了中断状态。
事实上join、sleep、wait都会响应中断,但是他们都会抛出InterruptedException异常,并且会重置中断状态。
LockSupport这一点上join、sleep、wait不同,所以LockSupport唤醒后,需要自己再进行判断唤醒的原因。
先执行unpark再执行park会怎么样?
如果先执行了unpark,再执行park,park就不会进行休眠,会消费掉unpark产生的permit。
但是假如现在执行了三次unpark,然后执行park,第一次执行park的时候,不会进行休眠,但是第二次执行park的时候会进入休眠状态,因为unpark的计数不能累加。
这一点和wait不同,如果先执行了notify,再执行wait,会进入休眠状态。
也就是park和unpark可以反着用,但是wait和notify不能反着用。
sleep、wait、LockSupport对比
Thread#sleep | Object#wait | LockSupport | |
---|---|---|---|
是否支持定时休眠 | 支持 | 支持 | 支持 |
是否支持永久休眠 | 不支持 | 支持 | 支持 |
是否必须与synchronized配合使用 | 否 | 是 | 否 |
是否会释放锁 | 否 | 是 | 否 |
是否支持其他线程将其唤醒 | 否 | 是 | 是 |
其他线程唤醒过程中是否必须按照顺序(先等待再唤醒) | 不支持唤醒 | 是 | 否(可以先unpark,再park) |
线程状态 | TIMED_WAITING | WAITING/TIMED_WAITING | WAITING/TIMED_WAITING |
中断后是否会抛出InterruptedException异常 | 是 | 是 | 否 |
中断后是否会重置状态状态 | 是 | 是 | 否 |