在JUC中的ReentrantLock的尝试获得锁有两种方式,分别是:
1.不响应中断
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
2.响应中断
public final void acquireInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (!tryAcquire(arg))
doAcquireInterruptibly(arg);
}
1处逻辑,虽然不立即响应中断,但当线程从park处继续执行后,会返回线程的interrupted状态,该方法会复位线程中断状态。
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
相关中断方法方法参考:
方法参数true/false即控制是否复位(重置)状态标记
从AQS源码中其返回的是调用Thread.interrupted(),也就是说返回中线程的中断标记后,会进行重置。
注意在线程内部如果捕获InterruptedException,该线程的中断标记会自动重置
可以这样理解,中断异常都不获处理了,说明用户或业务已经知道该线程中断过,也处理过,所以没有必要继续保留该状态了。
如下代码示例可以证明上述描述:
public class ThreadStopSenior {
public static void main(String[] args) throws InterruptedException {
Thread th1 = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
System.out.println("abc....");
System.out.println(Thread.currentThread().isInterrupted());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().isInterrupted());
//在异常时使用interruptedException,inTerrupted会自动复位,所以在循环时又为False
e.printStackTrace();
}
}
});
th1.start();
Thread.sleep(1000);
//中断线程th1
th1.interrupt();
Thread.sleep(2000);
}
}
执行的结果是线程th1一直无法结束,原因即为上面所说虽然执行了interrupt,但由于异常捕获,标记又恢复了,所以while条件是一直成立。
在AQS中的实现在返回Thread.interrupted()为true,会继续强制执行一次interrupt
static void selfInterrupt() {
Thread.currentThread().interrupt();
}
那AQS里为何不使用isInterrupt方法返回,原因是:
若当前线程被唤醒后(从LockSupport.park方法返回),若return Thread.currentThread.isinterrupt()方法,线程中断标志不会清除。之后,线程再次试图获取锁,若依旧没有获取到,会再次尝试调用LockSupport.park方法将自己挂起。但是 此时,线程中断标志位为true,而在该状态下LockSupport.park方法并不会生效,使得程序继续执行。若该线程始终获取不到锁,该线程将在acquireQueue方法的循环中空转,cpu有可能会出现100%
尝试从park的实现代码中查找证据,调用链:
- LockSupport
- class Parker : public os::PlatformParker
- class PlatformParker : public CHeapObj<mtInternal>
答案在park的实现中
void Parker::park(bool isAbsolute, jlong time) {
//原子交换,如果_counter > 0,则将_counter置为0,直接返回,否则_counter为0
if (Atomic::xchg(0, &_counter) > 0) return;
//获取当前线程
Thread* thread = Thread::current();
assert(thread->is_Java_thread(), "Must be JavaThread");
//下转型为java线程
JavaThread *jt = (JavaThread *)thread;
//如果当前线程设置了中断标志,调用park则直接返回,所以如果在park之前调用了interrupt就会直接返回
if (Thread::is_interrupted(thread, false)) {
return;
}
// 高精度绝对时间变量
timespec absTime;
//如果time小于0,或者isAbsolute是true并且time等于0则直接返回
if (time < 0 || (isAbsolute && time == 0) ) { // don't wait at all
return;
}
//如果time大于0,则根据是否是高精度定时计算定时时间
if (time > 0) {
unpackTime(&absTime, isAbsolute, time);
}
//进入安全点避免死锁
ThreadBlockInVM tbivm(jt);
//如果当前线程设置了中断标志,或者获取mutex互斥锁失败则直接返回
//由于Parker是每个线程都有的,所以_counter cond mutex都是每个线程都有的,
//不是所有线程共享的所以加锁失败只有两种情况,第一unpark已经加锁这时只需要返回即可,
//第二调用调用pthread_mutex_trylock出错。对于第一种情况就类似是unpark先调用的情况,所以
//直接返回。
if (Thread::is_interrupted(thread, false) || pthread_mutex_trylock(_mutex) != 0) {
return;
}
int status ;
//如果_counter大于0,说明unpark已经调用完成了将_counter置为了1,
//现在只需将_counter置0,解锁,返回
if (_counter > 0) { // no wait needed
_counter = 0;
status = pthread_mutex_unlock(_mutex);
assert (status == 0, "invariant");
OrderAccess::fence();
return;
}
OSThreadWaitState osts(thread->osthread(), false /* not Object.wait() */);
jt->set_suspend_equivalent();
// cleared by handle_special_suspend_equivalent_condition() or java_suspend_self()
assert(_cur_index == -1, "invariant");
//如果time等于0,说明是相对时间也就是isAbsolute是fasle(否则前面就直接返回了),则直接挂起
if (time == 0) {
_cur_index = REL_INDEX; // arbitrary choice when not timed
status = pthread_cond_wait (&_cond[_cur_index], _mutex) ;
} else { //如果time非0
//判断isAbsolute是false还是true,false的话使用_cond[0],否则用_cond[1]
_cur_index = isAbsolute ? ABS_INDEX : REL_INDEX;
//使用条件变量使得当前线程挂起。
status = os::Linux::safe_cond_timedwait (&_cond[_cur_index], _mutex, &absTime) ;
//如果挂起失败则销毁当前的条件变量重新初始化。
if (status != 0 && WorkAroundNPTLTimedWaitHang) {
pthread_cond_destroy (&_cond[_cur_index]) ;
pthread_cond_init (&_cond[_cur_index], isAbsolute ? NULL : os::Linux::condAttr());
}
}
//如果pthread_cond_wait成功则以下代码都是线程被唤醒后执行的。
_cur_index = -1;
assert_status(status == 0 || status == EINTR ||
status == ETIME || status == ETIMEDOUT,
status, "cond_timedwait");
#ifdef ASSERT
pthread_sigmask(SIG_SETMASK, &oldsigs, NULL);
#endif
//将_counter变量重新置为1
_counter = 0 ;
//解锁
status = pthread_mutex_unlock(_mutex) ;
assert_status(status == 0, status, "invariant") ;
// 使用内存屏障使_counter对其它线程可见
OrderAccess::fence();
// 如果在park线程挂起的时候调用了stop或者suspend则还需要将线程挂起不能返回
if (jt->handle_special_suspend_equivalent_condition()) {
jt->java_suspend_self();
}
}