ReentrantLock公平锁与非公平锁实现原理

ReentrantLock公平锁与非公平锁的实现原理

ReentrantLock是J.U.C包下的一个类,默认是非公平的

public class ReentrantLock implements Lock, java.io.Serializable {
    // 内部类
    private final Sync sync;

    public ReentrantLock() {
    	// 默认使用非公平锁
        sync = new NonfairSync();
    }
	
	// 可通过传入参数(true)的形式使用公平锁
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

其中内部类 Sync 继承了 AQS

abstract static class Sync extends AbstractQueuedSynchronizer {
	//...
}

NonfairSyncFairSync 都是继承自 Sync,也均是内部类

static final class NonfairSync extends Sync
static final class FairSync extends Sync

一. 非公平锁实现原理

我们先来看一下我们常用的使用方式 ReentrantLock 实现,一般都是直接 new 一个对象,然后通过 lock() 方法获取锁,它是怎么实现的呢,代码如下:

	// 1. 获取对象,默认非公平
	public ReentrantLock() {
        sync = new NonfairSync();
    }

	// 2. 调用lock() 方法
	public void lock() {
        sync.lock();
    }

	// 3. 调用非公平内部类实现的 Lock() 方法
	static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;
        
        final void lock() {
        	// 尝试获取锁
            if (compareAndSetState(0, 1))
            	// 获取成功后设置当前线程信息
                setExclusiveOwnerThread(Thread.currentThread());
            else
            	// 未获取到锁则执行以下方法
                acquire(1);
        }
		// 其他方法。。。
    }

	// acquire(1) 方法内部逻辑
	public final void acquire(int arg) {
        if (!tryAcquire(arg) && // 尝试获取锁
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))// 调用AQS 同步方法
            selfInterrupt();
    }

非公平锁的 acquire(int arg) ,逻辑比较简单,源码如下

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

		/**
         * Performs non-fair tryLock.  tryAcquire is implemented in
         * subclasses, but both need nonfair try for trylock method.
         */
        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            // 若锁状态为0,则直接执行获取锁逻辑
            if (c == 0) {
                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;
        }

二. 公平锁实现原理

接下来我们一起看下公平锁的源码有何不同

	// 传入 true 则会使用公平锁实现 FairSync
	public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }
	
	static final class FairSync extends Sync {
		// lock方法直接调用 acquire 方法
        final void lock() {
            acquire(1);
        }
        // ...
    }

acquire 方法是公用的,主要看一下内部的 tryAcquire 方法,直接上源码:

		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;
        }
    }

相信大家都已经看出来了它们的差异,接下来我再把它们不同的地方圈出来

三. 公平与非公平锁的对比

何为公平,何为非公平,在这个标榜着人人平等的社会,排队买东西大家都比较熟悉,不管是谁都得排队,先的来先买(除特殊情况)。

ReentrantLock也是相同的思想,引入了阻塞队列来实现排队等候,先来先执行,后来则排队(FIFO),所以是公平的。

那是什么是非公平呢,有人插队嘛,对吧,都插队了还谈什么公平呢,ReentrantLock的非公平锁就是不考虑阻塞队列的情况下直接获取锁。

看看源码上的区别大家就一目了然了:

		// 非公平锁
		final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

		// 公平锁
		final void lock() {
            acquire(1);
        }

非公平的进来先看看能不能拿到锁,如果拿到的话转身就溜,也不看看有没有其它线程在排队等待

再来看下 acquire 方法的流程

	public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

		// 公平锁
		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;
        }

		// 非公平锁
		protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
        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()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

可以看出来,非公平锁在 lock() 方法里多了直接获取锁的逻辑

if (compareAndSetState(0, 1))
	setExclusiveOwnerThread(Thread.currentThread());

在 tryAcquire(int acquires) 方法里少了判断阻塞的逻辑,而公平锁首先会考虑是否有线程在排序等候,主要方法 hasQueuedPredecessors()

// FairSync
if (!hasQueuedPredecessors() &&
    compareAndSetState(0, acquires)) {
    setExclusiveOwnerThread(current);
    return true;
}

// NonfairSync
if (compareAndSetState(0, acquires)) {
    setExclusiveOwnerThread(current);
    return true;
}

锁的释放公平与非公平是一样的

如有错误的地方还望大家指正

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值