解决了什么问题:
重入锁解决了同步方法调用另一个同步方法时死锁的问题(即方法A没有解锁的情况下 方法B可以取得锁 并在B归还锁之后 锁依然被A持有)
代码示例:
以下代码中使用两种重入方式 关键字synchronized 和 基于AQS的重入锁ReentrantLock
public static void main(String[] args) throws InterruptedException { Test test = new Test(); test.testA(); test.testC(); } //inner class public static class Test{ ReentrantLock lock = new ReentrantLock(); //使用synchronized重入锁 public synchronized void testA(){ System.out.println("testA"); testB(); } private synchronized void testB(){ System.out.println("testB"); } //使用ReentrantLock重入锁 public void testC(){ lock.lock(); System.out.println("testC"); testD(); lock.unlock(); } private void testD(){ lock.lock(); System.out.println("testD"); lock.unlock(); } }
代码分析:
test.testA() 使用synchronized 关键字, 进入方法可知, testA方法取得了test的对象锁(不太明白可以看我之前synchronized 文章), 然后在方法testA中调用testB方法, 此时对象锁未被释放, 但是synchronized 属于重入锁, 同一个线程中的testB依然可以获取test对象锁
test.testC() 使用ReentrantLock锁,同样达到同一线程重入的目的, 具体源码解析接下来讲
ReentrantLock源码分析:
lock()方法: 获取锁, 原理比较简单, 若没有线程占有锁则之间独占; 再判断是否有线程占有锁则判断是不是当前线程占有了, 若是当前线程占有则, 将AQS的state加1, 若非当前线程则假如AQS等待队列
类 ReentrantLock //获取锁 public void lock() { sync.lock(); } 类 ReentrantLock.NonfairSync final void lock() { //尝试原子操作获取锁 这是非公平锁特性 先尝试获取锁 获取不到再做判断 if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); //没有获取到锁 else acquire(1); } //获取锁 public final void acquire(int arg) { //若是当前线程获取锁 tryAcquire(arg)=true则直接走完代码 //若非当前线程获取锁 tryAcquire(arg)=false 则会acquireQueued(addWaiter(Node.EXCLUSIVE), arg) 将当前线程假如等待锁队列 if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); } //尝试获取锁 protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); } //非公平锁尝试获取实现 final boolean nonfairTryAcquire(int acquires) { //获取当前线程 final Thread current = Thread.currentThread(); //获取当前AQS状态 int c = getState(); //若当前状态为0 表示没有线程占有锁 则原子操作获取锁 //并将AQS独占线程设置为当前线程 if (c == 0) { if (compareAndSetState(0, acquires)) { //AQS独占线程设置为当前线程 setExclusiveOwnerThread(current); return true; } } //如果c!=0,即有线程获取了锁 则判断获取到锁的线程是否为当前线程 else if (current == getExclusiveOwnerThread()) { //预期设置AQS的state=state+acquires 在这里acquires值为1 int nextc = c + acquires; if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); //设置AQS状态为state=state+acquires setState(nextc); //返回获取锁成功 return true; } return false; }
unlock方法: 释放锁, 原理是利用AQS的state和exclusiveOwnerThread来判断, 若非当前线程释放锁直接抛出异常; 若是当前线程操作则需要对比state, 通俗的说就是在同一个线程中每一次lock()方法则state加1 每一次unlock()方法state减1
知道unlock()次数和lock()次数一样, state的值重新变成0, 则释放锁, 并将锁让给下一个等待中的线程
类 ReentrantLock //释放锁 public void unlock() { sync.release(1); } 类 ReentrantLock.Sync //释放锁 public final boolean release(int arg) { //tryRelease(arg)解释如下 //若是当前线程释放锁, 且AQS状态state等于0,tryRelease(arg)等于true 则唤醒当前线程,并通知后面等待线程 //若是当前线程释放锁, 且AQS状态state不等于0,tryRelease(arg)等于false 则只是将state减1 //若是非当前线程释放 抛出异常 if (tryRelease(arg)) { Node h = head; if (h != null && h.waitStatus != 0) unparkSuccessor(h); return true; } return false; } protected final boolean tryRelease(int releases) { //c=state-1 int c = getState() - releases; //若当前释放锁的线程非独占此锁的线程 则抛出异常 if (Thread.currentThread() != getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); boolean free = false; //如果state-1==0 则释放成功, 并设置独占线程为空, 以便下一个线程独占此锁 //若是重入锁 那么state-1!=0 则独占线程任然为当前线程 其他线程依然无法获取锁 if (c == 0) { free = true; //设置当前独占线程为空 setExclusiveOwnerThread(null); } //设置当前state=c=state-1 setState(c); //若是重入 c!=0 则free任然是false return free; }
总结:作为AQS的高级应用, 实现上并不复杂, 只是在AQS的基础上做了state的判断