揭下ReentrantLock神秘的面纱

14 篇文章 0 订阅
9 篇文章 0 订阅

结论

    先给出结论。综合起来,ReentrantLock是一个独占锁,可重入锁,且有公平锁和非公平锁之分。
    公平锁和非公平锁各自是怎么实现的呢?非公平锁中,某一线程调用lock()时就会使用CAS尝试获取锁(不管锁当前是否空闲),获取失败才会加入到队列中等待。而在公平锁中,某一线程调用lock()时,只有在当前锁是空闲时,且等待队列中没有其他线程才会使用CAS去获取,如果等待队列中有其他线程,那么它还是会加入到队列中,然后按顺序获取。那么我们可以想到,非公平锁的性能是肯定要比公平锁要好的,因为可能一个线程调用lock的时候,锁可能刚好释放,那么就可以被当前线程占用。若使用公平锁,则还需要去队列中通知下一个等待的线程,唤醒线程也是需要一定的时间的。也可能存在这样一种情况,当唤醒该线程的时候,刚好前面插队的线程已经用完锁并且释放了。

    而重入又是怎么实现的呢?可以看到syn继承了AQS,而AQS又继承了AbstractOwnableSynchronizer。在AbstractOwnableSynchronizer中有一个用于记录独占式获取锁时,最近一次获取锁的线程的字段。

public abstract class AbstractOwnableSynchronizer
    implements java.io.Serializable {
 /**
     * The current owner of exclusive mode synchronization.
     */
    private transient Thread exclusiveOwnerThread;

    然后在ReentrantLock 只需要判断调用lock()的线程是否和这里面记录的线程相等,若相等,则表明该线程要再次获取锁,ReentrantLock就将它的state+1(state 是实际上通过AQS来维护的),用来记录重入的次数。在release()的时候,state-1。state=0的时候,才是表示完全释放锁的时候,别的线程才能去获取锁。
    还需要注意的一点是,非公平锁和公平锁的tryLock()方法都是在调用时若锁可用,则用CAS去尝试获取,而忽略等待队列。但是对于tryLock(long timeout, TimeUnit unit)方法则是调用各自的tryAcquire()。所以总结起来就是在公平锁中也是有非公平地获取锁的方式的,也算是留个后路吧,毕竟非公平方式的性能更高

    ReentrantLock

public class ReentrantLock implements Lock, java.io.Serializable {

    private final Sync sync;

     //默认非公平锁
    public ReentrantLock() {
        sync = new NonfairSync();
    }


    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }


     //获取锁,非公平锁和公平锁实现不同。具体看下面各自的方法
    public void lock() {
        sync.lock();
    }


    public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }

    //尝试使用非公平的方式获取锁,调用的是Sync里面的方法(不区分是公平还是不公平),我的理解是可能给公平锁一个非公平获取锁的途径。即使是在公平锁中,如果当前锁是空闲的,那么会立即获取到该锁,不管该锁的等待队列中是否有线程在等待。
    public boolean tryLock() {
        return sync.nonfairTryAcquire(1);
    }

   //该方法实际上调用的是各自锁里面的tryAcquire()方法,跟上面的tryLock()不一样,可以保证公平性
    public boolean tryLock(long timeout, TimeUnit unit)
            throws InterruptedException {
        return sync.tryAcquireNanos(1, unit.toNanos(timeout));
    }


    public void unlock() {
        sync.release(1);
    }

    public boolean isHeldByCurrentThread() {
        return sync.isHeldExclusively();
    }

   public boolean isLocked() {
        return sync.isLocked();
    }
    //只保留了一些关键方法
    ......

    public String toString() {
        Thread o = sync.getOwner();
        return super.toString() + ((o == null) ?
                                   "[Unlocked]" :
                                   "[Locked by thread " + o.getName() + "]");
    }
}

    Sync(定义了公平锁和非公平锁都要实现的方法)

    继承AQS,定义了一些基本的方法,比如lock()等,然后分别由公平锁和非公平锁去实现

    abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = -5179523762034025860L;

       //子类各自实现
        abstract void lock();

//对于2种锁的tryLock()方法,都是调用该方法,tryLock()其实就是尝试获取锁,即在调用该方法的时候就用CAS获取看看能否获取成功,如果不能获取成功,则返回false
        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
            //保证重入,重入成功则state+1
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

//尝试释放,因为是可重入的,所以调用了之后还不一定可以释放锁,所以需要根据state来判断(state可以说是记录了重入次数)
        protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())
            //如果调用释放的锁不是当前占用的锁,那肯定是出错了
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) {//如果状态=0,表示这次释放完之后,锁就完全释放了,然后将free=true表示锁已经空闲
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }

        protected final boolean isHeldExclusively() {

            return getExclusiveOwnerThread() == Thread.currentThread();
        }

        // Methods relayed from outer class

        final Thread getOwner() {
            return getState() == 0 ? null : getExclusiveOwnerThread();
        }

        final int getHoldCount() {
            return isHeldExclusively() ? getState() : 0;
        }

        final boolean isLocked() {
            return getState() != 0;
        }

    }

    然后是非公平锁的实现

    非公平锁NonfairSync

 //非公平锁
    static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;

非公平锁在某一线程调用Lock方法时就尝试用CAS获取,而不用通过队列,进而提高效率,若获取失败,则调用AQS的acquire,实际上AQS的acquire方法内部调用的是NonfairSync自己实现的tryAcquire()
        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }

    然后是公平锁的实现

    公平锁FairSync

    static final class FairSync extends Sync {

 //公平锁在尝试获取锁时,只是简单的判断该锁是否释放,以及锁等待队列中有没有等待的线程,要是没有,则自己获取锁
        final void lock() {
            acquire(1);
        }

        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
//只有锁是空闲的时候才会尝试去获取,先判断等待队列中是否有线程,如果没有则用CAS去获取
            if (c == 0) {
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
//若当前锁的持有者是自己,则重入
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
//两者都不满足就获取失败,然后在AQS的acquire()里面会调用tryAcquire()。当tryAcquire()返回false的时候,当前线程就会被加入等待线程队列
            return false;
        }
    }

    再和代码结合起来看结论:综合起来,ReentrantLock是一个独占锁,可重入锁,且有公平锁和非公平锁之分。
    公平锁和非公平锁的区别则是,非公平锁中,某一线程调用lock()时就会使用CAS尝试获取锁(不管锁当前是否空闲),获取失败才会加入到队列中等待。而在公平锁中,某一线程调用lock()时,只有在当前锁是空闲时,且等待队列中没有其他线程才会使用CAS去获取,如果等待队列中有其他线程,那么它还是会加入到队列中,然后按顺序获取。那么我们可以想到,非公平锁的性能是肯定要比公平锁要好的,因为可能一个线程调用lock的时候,锁可能刚好释放,那么就可以被当前线程占用。若使用公平锁,则还需要去队列中通知下一个等待的线程,唤醒线程也是需要一定的时间的。也可能存在这样一种情况,当唤醒该线程的时候,刚好前面插队的线程已经用完锁并且释放了。

    而重入又是怎么实现的呢?可以看到syn继承了AQS,而AQS又继承了AbstractOwnableSynchronizer。在AbstractOwnableSynchronizer中有一个用于记录独占式获取锁时,最近一次获取锁的线程的字段。

public abstract class AbstractOwnableSynchronizer
    implements java.io.Serializable {
 /**
     * The current owner of exclusive mode synchronization.
     */
    private transient Thread exclusiveOwnerThread;

    然后在ReentrantLock 只需要判断调用lock()的线程是否和这里面记录的线程相等,若相等,则表明该线程要再次获取锁,ReentrantLock就将它的state+1(state 是实际上通过AQS来维护的),用来记录重入的次数。在release()的时候,state-1。state=0的时候,才是表示完全释放锁的时候,别的线程才能去获取锁。
    还需要注意的一点是,非公平锁和公平锁的tryLock()方法都是在调用时若锁可用,则用CAS去尝试获取,而忽略等待队列。但是对于tryLock(long timeout, TimeUnit unit)方法则是调用各自的tryAcquire()。所以总结起来就是在公平锁中也是有非公平地获取锁的方式的,也算是留个后路吧,毕竟非公平方式的性能更高

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值