• Java ReentrantLock锁源码走读


    多线程例子程序:两个线程累加共享变量,结果正确

    public class Test {
    
        static int count = 0;
        static ReentrantLock lock = new ReentrantLock();
    
        public static void main(String[] args) throws InterruptedException {
    
            Runnable runnable = new Runnable() {
                @Override
                public void run () {
                    try {
                        lock.lock();
                        for (int i = 0; i < 10000; i++) {
                            count++;
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                        lock.unlock();
                    }
                }
            };
    
            Thread thread1 = new Thread(runnable);
            Thread thread2 = new Thread(runnable);
            thread1.start();
            thread2.start();
            thread1.join();
            thread2.join();
    
            System.out.println(count);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    非公平锁加锁(即 lock.lock();)过程

    在这里插入图片描述

    1. ReentrantLock的lock方法 走到 同步器的 acquire(1)方法
    2. 走到aqs的acquire的方法,而其tryAcquire方法实现则是在NonfairSync类中
    3. NonfairSync执行tryAcquire方法又回到Sync的nonfairTryAcquire方法
    4. Sync的nonfairTryAcquire方法则如下

    相关代码 java.util.concurrent.locks.ReentrantLock.Sync#nonfairTryAcquire

    abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = -5179523762034025860L;
    
        /**
         * Performs non-fair tryLock.  tryAcquire is implemented in
         * subclasses, but both need nonfair try for trylock method.
         */
        @ReservedStackAccess
        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;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    1. 进行cas自旋获取,成功则设置当前线程获取到锁(此时state=1),否则一直自旋。
    2. 如果当前线程就是获取到锁的线程,则为可重入,此时 state > 1
    3. 其中 private volatile int state; volatile保证可见性、有序性,不保证原子性(CAS操作保证原子性)

    可以看到

    1. 当一个线程lock获取到锁后成功返回,并执行自己的代码(独占模式)
    2. 其它线程获取不到锁,则一直自旋在哪里

    非公平锁解锁( lock.unlock();)过程

    释放锁则相对简单,如下图所示

    在这里插入图片描述

    相关代码java.util.concurrent.locks.ReentrantLock.Sync#tryRelease

    @ReservedStackAccess
     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;
       }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    公平锁

    从上面非公平锁可以看到,加锁操作时看哪个线程cas抢到就行,至于谁抢到无所谓。可能有线程永远抢不到,即线程饥饿。这就出现了不公平的现象了

    new ReentrantLock(true);就得到了一个公平锁

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

    公平锁的加锁逻辑

    体现在java.util.concurrent.locks.ReentrantLock.FairSync#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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    1. 会先判断当前线程前面是否有等待的线程,即利用asq的等待队列进行判断

      • 如果有,则自己加入到链表尾部;返回false
      • 如果队列为空或者自己就是链表头;返回true
    2. hasQueuedPredecessors返回false,则可以进行cas获取锁操作了

    3. hasQueuedPredecessors返回true,那么自己则要排队了,先来后到(公平),不用cas操作了,直接加锁失败

    公平锁的释放锁逻辑

    同非公平锁

  • 相关阅读:
    Internet协议栈 TCP/IP模型 物理层、链路层、网络层、传输层、应用层的作用
    NMap 使用技巧总结(一)
    白话transformer(三):Q K V矩阵代码演示
    Redis | 在Java中操作Redis
    C++语法——make_heap、push_heap、pop_heap、sort_heap使用介绍
    基于Java的高校科研信息管理系统设计与实现(亮点:完整严谨的科研项目审批流程、多文件上传、多角色)
    想问一下如何用74LS151的实现(相关搜索:序列信号发生器)
    Python - OCR 之 pytesseract 简单使用记录
    【数据物语系列】 漫谈数据分布可视化分析
    含文档+PPT+源码等]基于ssm maven健身房俱乐部管理系统[包运行成功]Java毕业设计SSM项目源码
  • 原文地址:https://blog.csdn.net/qq_26437925/article/details/132920787