Java并发包中锁原理剖析(二)

1). 独占锁ReentrantLock

​ ReentrantLock 拥有内部抽象类Sync ,Sync 直接继承自AQS,它的子类NonfairSync 和FairSync 分别实现了非公平锁和公平锁策略。AQS 的state状态值代表的是线程获取锁的重入次数,默认情况下,state为0 表示当前锁没有被任何线程持有,当有线程第一次获取锁时会使用CAS设置state的值为1 成功后会将锁持有者设置为此线程。在此线程没有释放锁情况下,再次获取锁,会将state的值加一,这就是重入次数。在此线程释放锁时,会使用CAS将state值减一,如果减一后state的值为0,则此线程释放该锁。

  • public void lock():

    public void lock() {
        sync.lock();
    }
    

    调用了sync#lock方法 ,这里我们看NonfairSync的实现:

    final void lock() {
        if (compareAndSetState(0, 1))
            setExclusiveOwnerThread(Thread.currentThread());
        else
            acquire(1);
    }
    

    先尝试将state值设置为1,成功了将锁的持有者设为当前线程(一般为第一次获取锁时),否则调用AQS#acquire(1)(重入锁时)。 我们在上一篇已经介绍了AQS#acquire(int arg) ,其改变state的实现在tryAcquire(int arg)方法。我们看看nonfairTryAcquire(int arg)方法:

    final boolean nonfairTryAcquire(int acquires) {
        		// 获取当前线程
                final Thread current = Thread.currentThread();
       			 // 获取当前state值
                int c = getState();
        		// 如果为 0 ,则将state 设为 acquires ,再将锁的持有者设为当前线程
                if (c == 0) {
                    if (compareAndSetState(0, acquires)) {
                        setExclusiveOwnerThread(current);
                        return true;
                    }
                }
        		// 不为 0 并且当前线程是锁的持有者, 将当前的state加上acquires并设置为新的state
                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;
            }
    

    具体的逻辑都写了注解。再看FairSync 的实现:

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

    主要差别再hasQueuedPredecessors方法:

     public final boolean hasQueuedPredecessors() {
            // The correctness of this depends on head being initialized
            // before tail and on head.next being accurate if the current
            // thread is first in queue.
            Node t = tail; // Read fields in reverse initialization order
            Node h = head;
            Node s;
            return h != t &&
                ((s = h.next) == null || s.thread != Thread.currentThread());
        }
    

    在如上代码中,如果当前线程节点有前驱节点则返回住时,否则如果当前AQS队列为空或者当前线程节是AQS的第一个节点则返回false。其中如果ht则说明当前队列为空,直接返回false;如果h!=t并且snull则说明有一个元素将要作为AQS的第一个节点入队列(回顾上一节的内容,enq函数的第一个元素入队列是两步操作:首先创建一个哨兵头节点,然后将第一个元素插入哨兵节点后面,那么返回true,如果h!=t并且s!=null和s.thread!= Thread.cunentThread()则说明队列里面的第一个元素不是当前线程,那么返回true。这里可以知道,公平锁的机制是,阻塞队列前如果有线程在等待,则阻塞当前线程,类似于排队。

  • public void lockInterruptibly() throws InterruptedException

     public void lockInterruptibly() throws InterruptedException {
            sync.acquireInterruptibly(1);
        }
    
     public final void acquireInterruptibly(int arg)
                throws InterruptedException {
            if (Thread.interrupted())
                throw new InterruptedException();
            if (!tryAcquire(arg))
                doAcquireInterruptibly(arg);
        }
    

    lockInterruptibly方法会响应中断,当前线程在调用该方法时,如果其他线程调用了当前线程的interrupt方法,则会抛出InterruptedException异常。

  • public boolean tryLock() :

    public boolean tryLock() {
        return sync.nonfairTryAcquire(1);
    }
    

    直接调用的非公平策略

  • public boolean tryLock(long timeout, TimeUnit unit)

    public boolean tryLock(long timeout, TimeUnit unit)
            throws InterruptedException {
        return sync.tryAcquireNanos(1, unit.toNanos(timeout));
    }
    
    public final boolean tryAcquireNanos(int arg, long nanosTimeout)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        return tryAcquire(arg) ||
            doAcquireNanos(arg, nanosTimeout);
    }
    

    在指定时间没有获取锁,即返回。

  • public void unlock()

    public void unlock() {
        sync.release(1);
    }
    
    protected final boolean tryRelease(int releases) {
        int c = getState() - releases;
        if (Thread.currentThread() != getExclusiveOwnerThread())
            throw new IllegalMonitorStateException();
        boolean free = false;
        if (c == 0) {
            free = true;
            setExclusiveOwnerThread(null);
        }
        setState(c);
        return free;
    }
    

    当前线程不是锁的持有者抛出异常, 当前state值减去releases,如果为0时,锁释放。设置state的新值。

2).读写锁ReentrantReadWriteLock

​ 解决线程安全问题使用ReentrantLock就可以,但是ReentrantLock是独占锁,某时只有一个线程可以获取该锁,而实际中会有写少读多的场景,显然ReentrantLock满足不了这个需求,所以ReentrantReadWriteLock应运而生。ReentrantReadWriteLock采用读写分离的策略,允许多个线程可以同时获取读锁。

​ 读写锁的内部维护了一个ReadLock和一个WriteLock,它们依赖Sync实现具体功能。而Sync继承自AQS,并且也提供了公平和非公平的实现。下面只介绍非公平的读写锁实现。我们知道AQS中只维护了一个state状态,而ReentrantReadWriteLock则需要维护读状态和写状态,一个state怎么表示写和读两种状态呢?ReentrantReadWriteLock巧妙地使用state的高16位表示读状态,也就是获取到读锁的次数;使用低16位表示获取到写锁的线程的可重入次数。

1). WriteLock

  • public void lock()

    public void lock() {
        sync.acquire(1);
    }
    

    主要看:

    protected final boolean tryAcquire(int acquires) {
        /*
         * Walkthrough:
         * 1. If read count nonzero or write count nonzero
         *    and owner is a different thread, fail.
         * 2. If count would saturate, fail. (This can only
         *    happen if count is already nonzero.)
         * 3. Otherwise, this thread is eligible for lock if
         *    it is either a reentrant acquire or
         *    queue policy allows it. If so, update state
         *    and set owner.
         */
        Thread current = Thread.currentThread();
        int c = getState();
        int w = exclusiveCount(c);
        if (c != 0) {
            // (Note: if c != 0 and w == 0 then shared count != 0)
            if (w == 0 || current != getExclusiveOwnerThread())
                return false;
            if (w + exclusiveCount(acquires) > MAX_COUNT)
                throw new Error("Maximum lock count exceeded");
            // Reentrant acquire
            setState(c + acquires);
            return true;
        }
        if (writerShouldBlock() ||
            !compareAndSetState(c, c + acquires))
            return false;
        setExclusiveOwnerThread(current);
        return true;
    }
    

    writerShouldBlock 方法在公平锁和非公平锁实现不同:

    • NonfairSync :
    final boolean writerShouldBlock() {
        return false; // writers can always barge
    }
    
    • FairSync:

      final boolean writerShouldBlock() {
          return hasQueuedPredecessors();
      }
      
  • public boolean tryLock( )

    public boolean tryLock( ) {
        return sync.tryWriteLock();
    }
    
    final boolean tryWriteLock() {
        Thread current = Thread.currentThread();
        int c = getState();
        if (c != 0) {
            int w = exclusiveCount(c);
            if (w == 0 || current != getExclusiveOwnerThread())
                return false;
            if (w == MAX_COUNT)
                throw new Error("Maximum lock count exceeded");
        }
        if (!compareAndSetState(c, c + 1))
            return false;
        setExclusiveOwnerThread(current);
        return true;
    }
    

    上面的逻辑与tryAcquire()方法类似。

  • public boolean tryLock(long timeout, TimeUnit unit)

    public final boolean tryAcquireNanos(int arg, long nanosTimeout)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        return tryAcquire(arg) ||
            doAcquireNanos(arg, nanosTimeout);
    }
    
  • public void unlock()

    public void unlock() {
        sync.release(1);
    }
    
    protected final boolean tryRelease(int releases) {
        // 锁持有者是否为当前线程 , 不是则抛出异常
        if (!isHeldExclusively())
            throw new IllegalMonitorStateException();
        int nextc = getState() - releases;
        //   state 为 0 则释放锁
        boolean free = exclusiveCount(nextc) == 0;
        if (free)
            setExclusiveOwnerThread(null);
        // 设置新的state
        setState(nextc);
        return free;
    }
    

2).ReadLock

  • public void lock()

    protected final int tryAcquireShared(int unused) {
               
                Thread current = Thread.currentThread();
                int c = getState();
        		// 判断是否有线程是否持有写锁, 如果有判断是否是当前线程。如果有而不是当前线程返回-1;
                if (exclusiveCount(c) != 0 &&
                    getExclusiveOwnerThread() != current)
                    return -1;
        		// 获取锁共享的数量
                int r = sharedCount(c);
                if (!readerShouldBlock() &&
                    // 是否超过最大
                    r < MAX_COUNT &&
                    compareAndSetState(c, c + SHARED_UNIT)) {
                    // 如果 分享数量为 0 ,则当前线程是第一个持有读锁的线程
                    if (r == 0) {
                        firstReader = current;
                        firstReaderHoldCount = 1;
                    } else if (firstReader == current) {
                        firstReaderHoldCount++;
                    } else {
                        // 锁共享数量加一
                        HoldCounter rh = cachedHoldCounter;
                        if (rh == null || rh.tid != getThreadId(current))
                            cachedHoldCounter = rh = readHolds.get();
                        else if (rh.count == 0)
                            readHolds.set(rh);
                        rh.count++;
                    }
                    return 1;
                }
        		// 类似  但是是自旋获取
                return fullTryAcquireShared(current);
            }
    
  • public void unlock()

     public void unlock() {
                sync.releaseShared(1);
            }
    
     protected final boolean tryReleaseShared(int unused) {
                Thread current = Thread.currentThread();
         // 是否为第一个持有者
                if (firstReader == current) {
                    // assert firstReaderHoldCount > 0;
                    if (firstReaderHoldCount == 1)
                        firstReader = null;
                    else
                        firstReaderHoldCount--;
                } else {
                    HoldCounter rh = cachedHoldCounter;
                    if (rh == null || rh.tid != getThreadId(current))
                        rh = readHolds.get();
                    int count = rh.count;
                    if (count <= 1) {
                        readHolds.remove();
                        if (count <= 0)
                            throw unmatchedUnlockException();
                    }
                    --rh.count;
                }
                for (;;) {
                    int c = getState();
                    int nextc = c - SHARED_UNIT;
                    if (compareAndSetState(c, nextc))
                        // Releasing the read lock has no effect on readers,
                        // but it may allow waiting writers to proceed if
                        // both read and write locks are now free.
                        return nextc == 0;
                }
            }
    

    如以上代码所示,在无限循环里面,首先获取当前AQS状态值并将其保存到变量c,然后变量c被减去一个读计数单位后使用CAS操作更新AQS状态值,如果更新成功则查看当前AQS状态值是否为0,为0则说明当前己经没有读线程占用读锁,则tryReleaseShared返回true。然后会调用doReleaseShared方法释放一个由于获取写锁而被阻塞的线程,如果当前AQS状态值不为0,则说明当前还有其他线程持有了读锁,所以trγReleaseShared返回false。如果trγReleaseShared中的CAS更新AQS状态值失败,则自旋重试直到成功。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值