ReentrantLock-源码解析

ReentrantLock类注释

1、可重入互斥锁,意思是表示该锁能够支持一个线程对资源的重复加锁,该锁还支持获取锁的公平和非公平性选择。synchronized关键字隐式的支持重进入。
2、可以通过isHeldByCurrentThread判断当前线程是否拿到锁。
3、构造器接收fairness参数,fairness=true表示公平锁反之是非公平锁。
4、tryLock() 无参方法没有遵循公平性,是非公平的。
5、公平锁的吞吐量没有非公平锁高,需要不断的进行线程之间的切换

ReentrantLock类结构

public class ReentrantLock implements Lock, java.io.Serializable 

在这里插入图片描述
Lock所定义的方法。
在这里插入图片描述

  • lock():获取锁,调用该方法的线程会获取锁。
  • unlock(): 释放锁
  • tryLock(): 尝试非阻塞的获取锁,获取成功返回true,失败返回false。
  • tryLock(long,TimeUtil): 超时获取锁,有三种情况:当前线程在超时时间内获得了锁,当前线程在超时时间内被中断,超时时间结束,返回false。
  • lockInterruptedException(): 可中断的获取锁,所在获取过程中可以中断当前线程。

公平锁与非公平锁

简言之,如果在绝对的时间上,先对锁发起请求的那个线程会先获取锁,遵循FIFO原则,也就是等待时间最长的那个线程最先获取锁。

公平锁和非公平锁只是相对于获取锁的时候而言。

如果一个锁是公平的,那么锁获取的顺序符合时间顺序。当一个线程获取了同步状态就可以认为获取到了锁,刚释放锁的线程下次再获取锁的概率非常大,使其他线程在同步队列中等待,因此非公平锁的线程切换次数很少,这是相对公平锁而言,所以吞吐量上,非公平锁更有优势,所以默认情况下,ReentrantLock的构造就是非公平锁。

互斥锁

互斥锁:同一时刻下,只有一个线程可以拿到锁,其他线程只能在同步队列中等待,只有拿到锁的线程释放锁,其他线程才能去争抢;

构造方法


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

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

从构造器中可以看出,公平锁是依靠 FairSync 实现的,非公平锁是依靠 NonfairSync 实现的。

Sync同步器

Sync继承了AQS,lock方法留给FairSync和NonFairSync两个子类实现,他实现了AQS的tryRelease方法和isHeldExclusively方法。

abstract static class Sync extends AbstractQueuedSynchronizer{ 
        // 尝试释放锁
        protected final boolean tryRelease(int releases) {
            // releases一般是1,当前线程的同步状态-同步值
            int c = getState() - releases;
            // 当前线程没有持有锁,抛异常 
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            // 当前线程持有的锁都释放了
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            // c!=0代表是重入锁,锁没有释放完毕,前面已经用state-releases
            setState(c);
            return free; // return false,尝试释放锁失败
        }

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

加锁

先来看FairSync公平锁的lock方法和tryAcquire

  static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;
		// 尝试加锁,acquire方法是AQS的获取锁的方法,如果尝试获取锁失败,会进入同步队列中等待
        final void lock() {
            acquire(1);
        }
        /**
        AQS的acquire
        public final void acquire(int arg) {
	        if (!tryAcquire(arg) &&
	            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
	            selfInterrupt();
    	}
        **/

        /**
         * 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) {
             // hasQueuedPredecessors 是实现公平的关键
        // 会判断当前线程是不是属于同步队列的头节点的下一个节点(头节点是释放锁的节点)
        // 如果是(返回false),符合先进先出的原则,可以获得锁
        // 如果不是(返回true),则继续等待
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            // 如果获取锁的线程再次请求锁,将通天阁不状态值增加并返回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;
        }
    }

再来看NonFairSync非公平锁的加锁和尝试获取锁

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

        /**
         * Performs lock.  Try immediate barge, backing up to normal
         * acquire on failure.
         */
        final void lock() {
        // 通过判断当前线程是否获取锁的线程来决定获取操作是否成功,如果同步状态值成功修改,那就代表当前线程获取到了锁
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }
		// 调用的是父类Sync的尝试获取锁的方法
        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }

释放锁

unlock 释放锁的方法,底层调用的是 Sync 同步器的 release 方法,release 是 AQS 的方法,分成两步:

  1. 尝试释放锁,如果释放失败,直接返回 false;
  2. 释放成功,从同步队列的头节点的下一个节点开始唤醒,让其去竞争锁。

第一步就是我们上文中 Sync 的 tryRelease 方法(4.1),第二步 AQS 已经实现了。
释放锁通过LockSupport.unpark(s.thread);

unLock 的源码如下:

// 释放锁
public void unlock() {
    sync.release(1);
}
原文地址:https://www.cnblogs.com/itjiangpo/p/14181320.html