上一章仅介绍了 ReentrantLock 的常用方法以及公平锁、非公平锁的实现。这里对上一章做一些补充。主要是:
lock.lockInterruptibly()
lock.tryLock(long,TimeUnit)
(本篇讲述)lock.tryLock(long,TimeUnit) 和 lock.tryLock() 是不同的。
使用方法:
public class ReentrantLockDemo1 {ReentrantLock lock = new ReentrantLock();Runnable r = new Runnable() {@Overridepublic void run() {try {//尝试在2s内获取到锁if(!lock.tryLock(2,TimeUnit.SECONDS)){System.out.println(Thread.currentThread().getName()+" 2s内获取不到锁");return;}else{System.out.println(Thread.currentThread().getName()+" 2s内获取到了锁");}} catch (InterruptedException e) {//如果在限定时间内被中断,则响应中断,并放弃等待锁资源e.printStackTrace();System.out.println(Thread.currentThread().getName()+" 2s内还未获取到锁,且被中断退出等待");return;}try{//临界区System.out.println(Thread.currentThread().getName()+" 获取到了锁,进入临界区");}finally {//释放锁lock.unlock();}}};public static void main(String[] args) {ReentrantLockDemo1 demo = new ReentrantLockDemo1();Thread t1= new Thread(demo.r,"t1");//主线程先获取到锁demo.lock.lock();System.out.println(Thread.currentThread().getName()+" 获取到锁");t1.start();try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}//主线程等待一段时间后才释放锁System.out.println(Thread.currentThread().getName()+" 释放了锁");demo.lock.unlock();}}
上述代码运行结果:
main 获取到锁
t1 2s内获取不到锁
main 释放了锁Process finished with exit code 0
由于主线程持有锁 5 秒,而线程 t1 获取锁的限定时间为 2 秒,限定时间后没有获取到锁,放弃锁资源的等待。
如果让主线程中等待时间缩减为 Thread.sleep(50),运行结果如下:
main 获取到锁
main 释放了锁
t1 2s内获取到了锁
t1 获取到了锁,进入临界区Process finished with exit code 0
通过调用 lock.tryLock( long , TimeUnit ) 进入到了 AQS 的 tryAcquireNanos(int arg, long) 方法:
//ReentrantLock
public boolean tryLock(long timeout, TimeUnit unit)throws InterruptedException {return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
//AbstractQueuedSynchronizer
public final boolean tryAcquireNanos(int arg, long nanosTimeout)throws InterruptedException {if (Thread.interrupted())throw new InterruptedException();return tryAcquire(arg) ||doAcquireNanos(arg, nanosTimeout);
}
tryAcquireNanos(int , long) 是一个模板方法,且由于 tryAcquire() 方法需要子类实现,所以分为了公平与非公平锁两种情况。
在 doAcquireNanos() 方法中核心通过 LockSupport.parkNanos() 来定时唤醒并查看是否限时结束。
private boolean doAcquireNanos(int arg, long nanosTimeout)throws InterruptedException {//如果等待时间不合法,直接退出if (nanosTimeout <= 0L)return false;//计算截止时间final long deadline = System.nanoTime() + nanosTimeout;//封装到node结构中,并加到AQS的等待队列队尾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 GCfailed = false;return true;}//如果获取不到锁资源,计算剩余时间nanosTimeout = deadline - System.nanoTime();if (nanosTimeout <= 0L)//剩余时间没了,则退出等待return false;//如果还有剩余时间if (shouldParkAfterFailedAcquire(p, node) &&//如果剩余时间 > 自旋时间阈值nanosTimeout > spinForTimeoutThreshold)//将线程阻塞挂起直到剩余时间结束//1. 中途可以被interrupt()中断,并进入下方的抛出异常响应中断//2. 唤醒后,再次进入 for(;;) 循环,最后进行一次锁资源的获取LockSupport.parkNanos(this, nanosTimeout);if (Thread.interrupted())throw new InterruptedException();}} finally {if (failed)cancelAcquire(node);}
}
一般情况下,进入 doAcquireNanos() 将会先时获取一次锁,如果没获取到,则:
不论是自旋获取锁,还是阻塞等待,都可以检测到线程 interrupt() 的情况,如果中断标记为 true, 可以响应中断,即抛出 InterruptedException
异常。
上一篇:读写权限详解