当使用lockInterruptibly的时候,如果人为中断线程,排在这个线程之后的线程可能会被unpark两次。这样在业务代码中就会出现park不住的情况。
问题描述
按以下步骤执行:
- A线程lock
- B线程lockInterruptibly
- C线程lock
- 到现在为止A获得锁,B线程是等待队列头节点后的下一个节点,C在B之后。
- interrupt B线程,在cancelAcquire中的unparkSuccessor打断点。
- A线程unlock,在unparkSuccessor打断点。
- 然后让B线程和A线程一起走到unparkSuccessor中的最后一行LockSupport.unpark(s.thread);
此时就会发现B线程和A线程都要去unpark C线程,相当于C线程被unpark了两次。
因为C线程之前是睡着的,所以第一次unpark唤醒C线程,第二次unpark就被存起来了。如果在C里面调用unpark,就会出现park不住的情况。
实验代码
package org.example.poolexecutor;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;
public class Test {
public static void main(String[] args) throws InterruptedException {
ReentrantLock lock = new ReentrantLock();
Thread t1 = new Thread(() -> {
try {
lock.lock();
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 打断点
lock.unlock();
}
}, "t1");
Thread t2 = new Thread(() -> {
try {
lock.lockInterruptibly();
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "t2");
Thread t3 = new Thread(() -> {
try {
lock.lock();
System.out.println("hi");
//
LockSupport.park();
LockSupport.park();
System.out.println("hi");
} finally {
lock.unlock();
}
}, "t3");
t1.start();
t2.start();
t3.start();
t2.interrupt();
Thread.sleep(1000000000);
}
}
一定要先让B走到unparkSuccessor,再让A走到unparkSuccessor。