锁中断的概念
个人理解就是在一定时间内如果线程还未能获取到锁,我们可以对其进行中断处理并且线程能感知到这一行为的发生(这里的中断并不是说立刻停止线程而是让线程感知到然后我们在进行一些相应的处理比如break、return)
synchronized不支持锁的中断
public class SynchronizedIntercept {
final static Object synchronizedObj=new Object();
public static void main(String[] args) throws InterruptedException {
Thread thread01=new Thread(new Runnable() {
@Override
public void run() {
synchronized (synchronizedObj) {
try {
Thread.sleep(Integer.MAX_VALUE);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
//先启动线程01,然后一直占用锁
thread01.start();
Thread.sleep(1000L);
Thread thread02=new Thread(new Runnable() {
@Override
public void run() {
synchronized (synchronizedObj) {
System.out.println("=========我是线程thread02======");
}
}
});
//然后启用线程2,让其永远也无法获取到锁
thread02.start();
Thread.sleep(1000L);
//对线程进行中断
thread02.interrupt();
//打印线程的状态
System.out.println(thread02.getState());//BLOCKED
}
}
我们发现thread02.interrupt()操作不起任何作用,这也验证synchronized不支持锁的中断这一结论
ReentrantLock中lockInterruptibly()支持中断
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockInterceptor {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new RunIt());
Thread t2 = new Thread(new RunIt());
t1.start();
Thread.sleep(2000L);
t2.start();
t2.interrupt();
}
}
class RunIt implements Runnable{
private static Lock lock = new ReentrantLock();
@Override
public void run() {
try {
runJob();
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName()+" Interrrupted from runJob.");
}
}
private void runJob() throws InterruptedException{
lock.lockInterruptibly();
System.out.println(Thread.currentThread().getName()+" 到此一游....");
try{
System.out.println(Thread.currentThread().getName() + " running");
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName() + " finished");
}catch(InterruptedException e){
System.out.println(Thread.currentThread().getName() + " interrupted");
}finally{
lock.unlock();
}
}
}
不要将lock()、lockInterruptibly()写在try()方法里面,因为如果写在里面,此时线程并没有得到锁只是阻塞在这里,然后如果线程被中断,那么将执行finally{lock.unlock(); }代码。由于没有获取到锁却调用释放锁的方法,代码肯定会报异常的
lockInterruptibly()支持中断源码分析
上篇文章我们介绍过lock()方法,知道了它是不支持中断的,因为就算线程被中断,lock()只会继续尝试获取锁而不会直接跳出循环,我们看下lockInterruptibly()是如何处理的
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
public final void acquireInterruptibly(int arg) throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (!tryAcquire(arg))
doAcquireInterruptibly(arg);
}
我们可以看到在试图获取锁的时候最先就判断是否被中断,如果被中断就直接抛异常。如果没有获取到锁则执行doAcquireInterruptibly(arg)
private void doAcquireInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.EXCLUSIVE);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return;
}
//这里才是重点,如果从park()中被中断唤醒则直接抛异常跳出死循环
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
lockInterruptibly()获取不到锁会调用doAcquireInterruptibly(),在被阻塞的情况下被唤醒那么将直接抛出throw new InterruptedException();来中断循环。
lock()获取不到锁会调用acquireQueued().
我们可以看到在park后然后被中断唤醒后并没有跳出循环,这也是为啥lock()不支持中断的原因.