玩转并发-ReentrantLock

概述

eentrantLock重入锁,是实现Lock接口的一个类,也是在实际编程中使用频率很高的一个锁,支持重入性,表示能够对共享资源能够重复加锁,即当前线程获取该锁再次获取不会被阻塞。在java关键字synchronized隐式支持重入性(关于synchronized可以看这篇文章),synchronized通过获取自增,释放自减的方式实现重入。与此同时,ReentrantLock还支持公平锁和非公平锁两种方式。那么,要想完完全全的弄懂ReentrantLock的话,主要也就是ReentrantLock同步语义的学习:1. 重入性的实现原理;2. 公平锁和非公平锁。

重入性的实现原理

要想支持重入性,就要解决两个问题:1. 在线程获取锁的时候,如果已经获取锁的线程是当前线程的话则直接再次获取成功;2. 由于锁会被获取n次,那么只有锁在被释放同样的n次之后,该锁才算是完全释放成功通过这篇文章,我们知道,同步组件主要是通过重写AQS的几个protected方法来表达自己的同步语义。针对第一个问题,我们来看看ReentrantLock是怎样实现的,以非公平锁为例,判断当前线程能否获得锁为例,核心方法为nonfairTryAcquire:

final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    //1. 如果该锁未被任何线程占有,该锁能被当前线程获取
	if (c == 0) {
        if (compareAndSetState(0, acquires)) {
        	//设置持有锁的线程为当前线程
            setExclusiveOwnerThread(current);
            return true;
        }
    }
	//2.若被占有,检查占有线程是否是当前线程
    else if (current == getExclusiveOwnerThread()) {
		// 3. 再次获取,计数加一
        int nextc = c + acquires;
        if (nextc < 0) // overflow
        	//nextc小于0,说明锁可重入溢出
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}

为了支持重入性,在第二步增加了处理逻辑,如果该锁已经被线程所占有了,会继续检查占有线程是否为当前线程,如果是的话,同步状态加1返回true,表示可以再次获取成功。每次重新获取都会对同步状态进行加一的操作,那么释放的时候处理思路是怎样的了?(依然还是以非公平锁为例)核心方法为tryRelease:

protected final boolean tryRelease(int releases) {
	//1. 同步状态减1
    int c = getState() - releases;
    //释放锁的线程必须为当前线程,否则抛出异常
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    if (c == 0) {
		//2. 只有当同步状态为0时,锁成功被释放,返回true
        free = true;
        //设置持有锁的线程为null
        setExclusiveOwnerThread(null);
    }
	// 3. 锁未被完全释放,返回false
    setState(c);
    return free;
}

公平锁与非公平锁

ReentrantLock支持两种锁:公平锁非公平锁。何谓公平性,是针对获取锁而言的,如果一个锁是公平的,那么锁的获取顺序就应该符合请求上的绝对时间顺序,满足FIFO。ReentrantLock的构造方法无参时是构造非公平锁,源码为:

public ReentrantLock() {
    sync = new NonfairSync();
}

另外还提供了另外一种方式,可传入一个boolean值,true时为公平锁,false时为非公平锁,源码为:

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

非公平锁的获取

final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            //获取当前锁的状态
            int c = getState();
            //锁的状态为0,说明此时没有线程占有锁
            if (c == 0) {
            	//进行CAS操作,设置值
                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;
        }

公平锁的获取:

protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
            	//此处与非公平锁的获取不同,增加了hasQueuedPredecessors的逻辑判断
            	//该方法用来判断当前节点在同步队列中是否有前驱结点
            	//如果有前驱结点说明有线程比当前线程更早地请求资源,根据公平性,当前线程请求资源失败
            	//如果没有,再进行后面的判断
                if (!() &&
                    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默认选择的是非公平锁,则是为了减少一部分上下文切换,保证了系统更大的吞吐量

剖析ReentrantLock源码

ReentrantLock内有三个内部类Sync,FairSync,NonfairSync,都是基于AQS实现的:
玩转并发-ReentrantLock
先复习下AQS中state:
玩转并发-ReentrantLock
state为0时表示锁是空闲,可以获取锁,当大于0时表示获得锁。 独占锁时大于0表示锁的重入次数,共享锁时,state共当前共享线程个数。


Sync类

AQS中建议继承AQS子类被推荐定义为自定义同步组件的静态内部类

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

      	//抽象lock方法
        abstract void lock();

        /**
         * Performs non-fair tryLock.  tryAcquire is implemented in
         * subclasses, but both need nonfair try for trylock method.
         */
        //该TryAquire默认给非公平锁使用
        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            //判断锁的状态
            int c = getState();
            //锁为空闲
            if (c == 0) {
            	//进行CAS操作,设置当前线程持有锁
                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;
        }
		
		//释放锁
        protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            //释放锁的线程必须为持有锁的线程
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) {
            	//state为0才能释放锁
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }
		
		//如果当前线程同步是以独占的方式进行的,则返回true
        protected final boolean isHeldExclusively() {
            // While we must in general read state before owner,
            // we don't need to do so to check if current thread is owner
            return getExclusiveOwnerThread() == Thread.currentThread();
        }
	
		//新生成的条件
        final ConditionObject newCondition() {
            return new ConditionObject();
        }

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

        /**
         * Reconstitutes the instance from a stream (that is, deserializes it).
         */
         //自定义饭序列化逻辑
        private void readObject(java.io.ObjectInputStream s)
            throws java.io.IOException, ClassNotFoundException {
            s.defaultReadObject();
            setState(0); // reset to unlocked state
        }
    }
NonfairSync类

NonfairSync继承自Sync

 /**
     * Sync object for non-fair locks
     */
    static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;

        /**
         * Performs lock.  Try immediate barge, backing up to normal
         * acquire on failure.
         */
         //首先基于AQS进行CAS操作,将0->1.若成功,则获取锁成功
         //否则,执行AQS的正常同步状态获取逻辑
        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

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

优先进行CAS操作,这已经体现出了非公平锁的特点了。因为此时可能有N+1个线程正在获得锁,其中一个线程已经获得锁,释放的瞬间,恰好被新的线程抢到,而不是那些还在排队的N个线程。

FairSync类

FairSync类同样继承自Sync类

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

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

        /**
         * Fair version of tryAcquire.  Don't grant access unless
         * recursive call or no waiters or is first.
         */
        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;
        }
    }
构造
//默认无参构造为创造非公平锁
public ReentrantLock() {
        sync = new NonfairSync();
    }
 //传入大于0的参数,则构造公平锁
public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

锁持有者管理者AbstractOwnableSynchronizer

直接剖析源码



package java.util.concurrent.locks;

/**
 * A synchronizer that may be exclusively owned by a thread.  This
 * class provides a basis for creating locks and related synchronizers
 * that may entail a notion of ownership.  The
 * {@code AbstractOwnableSynchronizer} class itself does not manage or
 * use this information. However, subclasses and tools may use
 * appropriately maintained values to help control and monitor access
 * and provide diagnostics.
 *
 * @since 1.6
 * @author Doug Lea
 */
 //一个线程可能独占一个同步锁,AbstractOwnableSynchronizer用于表示锁与持有者之间的关系;AbstractOwnableSynchronizer不管理这种关系,通过子类进行管理
public abstract class AbstractOwnableSynchronizer
    implements java.io.Serializable {

    /** Use serial ID even though all fields transient. */
    private static final long serialVersionUID = 3737899427754241961L;

    /**
     * Empty constructor for use by subclasses.
     */
    protected AbstractOwnableSynchronizer() { }

    /**
     * The current owner of exclusive mode synchronization.
     */
     //独占模式,锁的持有者
    private transient Thread exclusiveOwnerThread;

    /**
     * Sets the thread that currently owns exclusive access.
     * A {@code null} argument indicates that no thread owns access.
     * This method does not otherwise impose any synchronization or
     * {@code volatile} field accesses.
     * @param thread the owner thread
     */
     //设置锁持有者
    protected final void setExclusiveOwnerThread(Thread thread) {
        exclusiveOwnerThread = thread;
    }

    /**
     * Returns the thread last set by {@code setExclusiveOwnerThread},
     * or {@code null} if never set.  This method does not otherwise
     * impose any synchronization or {@code volatile} field accesses.
     * @return the owner thread
     */
     //得到锁的持有者
    protected final Thread getExclusiveOwnerThread() {
        return exclusiveOwnerThread;
    }
}