在这种情况下,一个线程联连续两次获取同一把锁,这是允许的。但是需要注意的是,如果同一个线程多次获的锁,那么在释放是也要释放相同次数的锁。如果释放的锁少了,相当于该线程依然持有这个锁,那么其他线程就无法访问临界区了。释放的次数多了也会抛出java.lang.IllegalMonitorStateException异常。
除了使用上的灵活,ReentrantLock还提供了一些高级功能如中断。限时等待等。
中断响应对用synchrozide来说,如果一个线程在等待,那么结果只有两种情况,要么获得这把锁继续执行下去要么一直等待下去。而使用重入锁,提供了另外一种可能,那就是线程可以被中断。也就是说在这里可以取消对锁的请求。这种情况对解决死锁是有一定帮组的。
下面代码产生了一个死锁,但是我们可以通过锁的中断,解决这个死锁。
public class ReentrantLockDemo implements Runnable { //重入锁ReentrantLock public static ReentrantLock lock1 = new ReentrantLock(); public static ReentrantLock lock2 = new ReentrantLock(); int lock; public ReentrantLockDemo(int lock) { this.lock = lock; } @Override public void run() { try { if (lock == 1) { lock1.lockInterruptibly(); Thread.sleep(500); lock2.lockInterruptibly(); System.out.println("this is thread 1"); } else { lock2.lockInterruptibly(); Thread.sleep(500); lock1.lockInterruptibly(); System.out.println("this is thread 2"); } } catch (Exception e) { //e.printStackTrace(); } finally { if (lock1.isHeldByCurrentThread()) { lock1.unlock();//释放锁 } if (lock2.isHeldByCurrentThread()) { lock2.unlock(); } System.out.println(Thread.currentThread().getId() + ":线程退出"); } } public static void main(String[] args) throws InterruptedException { ReentrantLockDemo r1 = new ReentrantLockDemo(1); ReentrantLockDemo r2 = new ReentrantLockDemo(2); Thread t1 = new Thread(r1); Thread t2 = new Thread(r2); t1.start(); t2.start(); Thread.sleep(1000); //t2线程被中断,放弃锁申请,释放已获得的lock2,这个操作使得t1线程顺利获得lock2继续执行下去; //若没有此段代码,t2线程没有中断,那么会出现t1获取lock1,请求lock2,而t2获取lock2,请求lock1的相互等待死锁情况 t2.interrupt(); } }
线程t1和t2启动后,t1先占用lock1然后在请求lock2;t2先占用lock2,然后请求lock1,因此很容易形成线程之间的相互等待。着这里使用的是ReenTrantLock提供了一种能够中断等待锁的线程的机制,通过lock.lockInterruptibly()来实现这个机制。
最后由于t2线程被中断,t2会放弃对lock1的1请求,同时释放lock2。这样可以使t1继续执行下去,结果如下图
锁申请等待限时除了等待通知以外,避免死锁还有另外一种方式,那就是限时等待。通过给定一个等待时间,让线程自动放弃。
public class TimeLockDemo implements Runnable { private static ReentrantLock reentrantLock = new ReentrantLock(); @Override public void run() { try { if (reentrantLock.tryLock(5, TimeUnit.SECONDS)) { Thread.sleep(6000); } else { System.out.println("Gets lock failed"); } } catch (InterruptedException e) { e.printStackTrace(); } finally { if (reentrantLock.isHeldByCurrentThread()){ reentrantLock.unlock(); } } } public static void main(String[] args) { TimeLockDemo demo1 = new TimeLockDemo(); TimeLockDemo demo2 = new TimeLockDemo(); Thread t1 = new Thread(demo1); Thread t2 = new Thread(demo2); t1.start(); t2.start(); } }