12、ReentrantLock中的公平锁和非公平锁的原理

ReentrantLock

ReentrantLock内部是通过AQS实现锁的功能,有公平锁和非公平锁两种实现。

  1. 公平锁,即锁的获取顺序按线程申请锁的先后顺序。
  2. 非公平锁,当一个线程t1申请锁时,锁刚好释放。即使已有其他线程在t1之前申请锁排队,线程t1还是会获取锁。这样减少了线程的等待唤醒的可能,减少上下文切换带来的开销。因为获取锁的顺序和申请顺序可能不一致所以叫非公平锁。

前置技能(先了解前置技能才好看懂)

  1. AQS
  2. CLH

ReentrantLock中的Sync

Sync是个抽象类,非公平锁和公平锁都基于这个类实现。这里实现了非公平的占用锁方法。

 abstract static class Sync extends AbstractQueuedSynchronizer {
        ...
        /**
         *非公平锁的占用方法
         */
        final boolean nonfairTryAcquire(int acquires) {
            //获取当前的线程
            final Thread current = Thread.currentThread();
            //获取当前的锁占用状态
            int c = getState();
            //state==0则说明没有线程占用锁
            if (c == 0) {
                //此时会直接把锁给当前线程,而不去判断CLH队列中的是否已有等待线程。
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            //这里是重入锁的处理,当前线程重入锁,state+1
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

        ...
    }

非公平锁

ReentrantLock中的非公平锁实现

static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;

       
        final void lock() {
            //当前线程进来先直接用cas尝试占用锁,失败再调用acquire
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }
        
        //acquire会调用tryAcquire判断占用锁是否成功,这里直接调用了Sync的非公平锁处理方法
        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
}

公平锁

static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;

        final void lock() {
            acquire(1);
        }

        /**
         *公平锁的处理
         * 
         */
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            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;
            }
            return false;
        }
    }


总结

  1. 非公平锁减少了线程发生等待唤醒的可能,节省了上下文切换的开销。
  2. 公平锁适合锁持有事件较长或者线程申请锁的间隔事件相对长的情况。
  3. 总的来所,公平锁的开销比非公平锁大,所以ReentrantLock默认支持的是非公平锁。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值