AQS之可重入锁ReentrantLock原理

摘要:
intc=getState();获取)){setExclusiveOwnerThread(当前);returntrue;arg))selfInterrupt();获取)){setExclusive OwnerThread;tryAcquire(参数)&

一、经典故事

村子里面,有一口井水。水质很好,村民们都想打井里的水。
村长这时就制定了规则:
井边安排一个看井人,维护打水的秩序。
打水时,以家庭为单位,哪个家庭任何人先到井边,就可以先打水,而且如果一个家庭占到了打水权,其家人这时候过来打水不用排队。
而那些没有抢占到打水权的人,一个一个挨着在井边排成一队,先到的排在前面。
二、图析ReentrantLock
/**
     * Creates an instance of {@code ReentrantLock}.
     * This is equivalent to using {@code ReentrantLock(false)}.
     */
    public ReentrantLock() {
        sync = new NonfairSync();
    }

    /**
     * Creates an instance of {@code ReentrantLock} with the
     * given fairness policy.
     *
     * @param fair {@code true} if this lock should use a fair ordering policy
     */
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

由构造函数中可知,ReentrantLock默认采用的是非公平锁模式,之所以默认才用这种模式,是因为:

非公平锁减少线程挂起的几率,后来的线程有一定的几率直接获取锁。

1. 非公平模式:
 
        final void lock() {
            if (compareAndSetState(0, 1)) // CAS尝试获取锁权
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }
        
        public final void acquire(int arg) {
          if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
        }

        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) {
                // 这里区别公平锁,直接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;
        }
 
AQS之可重入锁ReentrantLock原理第1张

 2. 公平锁模式:

     final void lock() {
            // 按部就班,看排队情况
            acquire(1);
        }
        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) {
                // 前面没人排队才CAS尝试获取锁
                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;
        }

AQS之可重入锁ReentrantLock原理第2张

 至于:

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

三、比对打水的故事

问看井人:空闲情况下,所有家庭问的结果,只能有一个成功。(CAS)
可重入锁:家庭为单位(每一个人同属一个线程),家庭获取到了打水权,家庭里所有人都获取到了打水权。
非公平模式:新到的家庭代表,直接去问看井人井是否空闲了
                             1. 空闲了就获取打水权
                             2. 不空闲,再看一眼有没有人在打水?
                                     2.1 没人,再去问看井人
                                     2.2 有人,是不是自家人?是则去打水,否则去排队
公平锁模式:新到的人(比如叫张三),先肉眼看是否有人在打水?
                             1.   没人的话再看前面有没有人排队
                                          1.1 有人排队,乖乖的去排队
                                          1.2 没人排队,去问看井人决定是否成功获取打水权
                             2.   有人在打水,问问打水人跟张三是否一家的?
                                           2.1 是,可以来打水
                                           2.2 否,去排队

 参考资料:

https://www.jianshu.com/p/3204049fbf14

https://www.jianshu.com/p/f584799f1c77

免责声明:文章转载自《AQS之可重入锁ReentrantLock原理》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇root和alias用法PowerDesigner(数据库迁移)下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

1. Linux驱动开发之开篇--Makefile

基本Makefile假设现在有3个文件,file2.h是函数声明,file2.c是函数定义,文件file1.c调用file2.c中的函数。则Makefile文件的编写如下: helloworld:file1.o file2.o gcc file1.o file2.o -o helloworld file1.o:file1.c file2.h...

.NetCore3.1获取文件并重新命名以及大批量更新及写入数据

using Microsoft.AspNetCore.Mvc; using MySql.Data.MySqlClient; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using...

【转载】51单片机data,bdata,idata,xdata使用注意事项

"51单片机编程在不同内存空间data xdata bdata定义变量的注意事项": 关键词:51 单片机 编程 不同 内存空间 data xdatabdata 定义 变量 注意事项 1、data区空间小,所以只有频繁用到或对运算速度要求很高的变量才放到data区内,比如for循环中的计数值。 2、data区内最好放局部变量。 因为局部变量的空间是可以...

MSBuild 常用命令(Copy,Zip)

Copy 1.下面的示例将 MvcApplication2 项集合中的项复制到 c:MyProjectMvcApplication2 文件夹中。 所有文件放入一个文件夹中,无层级。 <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemG...

WinDbg调试分析 asp.net站点 CPU100%问题

 公司为了节省成本,最近有一批服务器降了配置,CPU从8核降到了2核。本身是小站点,访问量也不高,CPU总是会飙到100%而且可以一直持续几个小时,直接强制结束进程可以维持几个小时,几个小时后又一样,运维那边总是受到cpu的警告短信很是苦恼,按理来说就算降低了配置也不至于会让CPU一直100%。    以下就分享本次使用 WinDbg 找出 CPU 100...

Nginx事件管理之事件处理流程

1. 概述 事件处理要解决的两个问题: "惊群" 问题,即多个 worker 子进程监听相同端口时,在 accept 建立新连接时会有争抢,引发不必要的上下文切换, 增加系统开销。 负载均衡问题。 这两个问题的解决需要依靠 Nginx 的 post 事件处理机制。Nginx 设计了两个 post 队列,一个是由被触发的监听连接的读事 件构成的 ngx_...