ReentrantLock详解(公平锁)

一、概述
ReentrantLock是一种基于AQS框架的应用实现,是JDK中的一种线程并发访问的同步
手段,它的功能类似于synchronized是一种互斥锁,可以保证线程安全。而且它具有比
synchronized更多的特性,比如它支持手动加锁与解锁,支持加锁的公平性。
AQS定义了一套多线程访问共享资源的同步器框架,是一个依赖状态(state)的同步器
ReentrantLock内部定义Sync实现了AbstractQueuedSynchronizer,公平锁、非公平锁都实现Sync
公平锁 FairSync
非公平锁 NonfairSyn(默认创建)

获取锁的逻辑:

  1. 自旋
  2. 获取锁
  3. 没有获取到锁
  4. 加入阻塞队列(CLH队列可认为双向链表)
  5. 设置前驱节点信号量-1(代表可被唤醒的)
  6. 阻塞当前线程

aqs 中node节点分为独占、共享,node节点
在这里插入图片描述
整体思维导图:
在这里插入图片描述

二、源码剖析

  lock()获取锁	  
        final void lock() {
            this.acquire(1);
        }
//AbstractQueuedSynchronizer进入acquire()
    public final void acquire(int var1) {
        if (!this.tryAcquire(var1) && this.acquireQueued(this.addWaiter(AbstractQueuedSynchronizer.Node.EXCLUSIVE), var1)) {
            selfInterrupt();
        }

    }

1、tryAcquire(var1)获取锁

        protected final boolean tryAcquire(int var1) {
            Thread var2 = Thread.currentThread();
            int var3 = this.getState();
            if (var3 == 0) {
            	//判断当前线程是否在阻塞队列中、不在设置节点thread为当前线程、返回true获取到锁
                if (!this.hasQueuedPredecessors() && this.compareAndSetState(0, var1)) {
                    this.setExclusiveOwnerThread(var2);
                    return true;
                }
                //判断当前节点属性thread是否是当前线程,是设置state自增(说明reentrantlock可重入)
            } else if (var2 == this.getExclusiveOwnerThread()) {
                int var4 = var3 + var1;
                if (var4 < 0) {
                    throw new Error("Maximum lock count exceeded");
                }
                this.setState(var4);
                return true;
            }

            return false;
        }

3、addWaiter(node)没有获取到锁加入队列、所有node节点都是独占模式

    private AbstractQueuedSynchronizer.Node addWaiter(AbstractQueuedSynchronizer.Node var1) {
        AbstractQueuedSynchronizer.Node var2 = new AbstractQueuedSynchronizer.Node(Thread.currentThread(), var1);
        AbstractQueuedSynchronizer.Node var3 = this.tail;
        //如果尾节点不为空追加到尾部、考虑到多线程并发有可能加不上进入到enq
        if (var3 != null) {
            var2.prev = var3;
            //采用cas算法保证原子性
            if (this.compareAndSetTail(var3, var2)) {
                var3.next = var2;
                return var2;
            }
        }
		
        this.enq(var2);
        return var2;
    }
    
        private AbstractQueuedSynchronizer.Node enq(AbstractQueuedSynchronizer.Node var1) {
        //采用自旋、最终保证多线程情况下节点一定可以追加到尾部
        while(true) {
            AbstractQueuedSynchronizer.Node var2 = this.tail;
            if (var2 == null) {
                if (this.compareAndSetHead(new AbstractQueuedSynchronizer.Node())) {
                    this.tail = this.head;
                }
            } else {
            	//设置当前节点前驱、注意此刻任意节点前驱一定有值,考虑到时间片的切换、next有可能没有设置好,这就是unparkSuccessor()为什么采用尾部遍历唤醒线程
                var1.prev = var2;
                if (this.compareAndSetTail(var2, var1)) {
                    var2.next = var1;
                    return var2;
                }
            }
        }
    }
    

4、acquireQueued()设置信号量、阻塞线程

inal boolean acquireQueued(AbstractQueuedSynchronizer.Node var1, int var2) {
        boolean var3 = true;

        try {
            boolean var4 = false;
			//采用自旋
            while(true) {
                AbstractQueuedSynchronizer.Node var5 = var1.predecessor();
                //获取锁
                if (var5 == this.head && this.tryAcquire(var2)) {
                    this.setHead(var1);
                    var5.next = null;
                    var3 = false;
                    boolean var6 = var4;
                    return var6;
                }
				//设置前驱节点信号量-1  阻塞当前线程
                if (shouldParkAfterFailedAcquire(var5, var1) && this.parkAndCheckInterrupt()) {
                    var4 = true;
                }
            }
        } finally {
            if (var3) {
                this.cancelAcquire(var1);
            }

        }
    }

5、释放锁

 public void unlock() {
        this.sync.release(1);
    }



    public final boolean release(int var1) {
    	//释放锁  就是把state设置state-var1,把当前节点thread设置null便于gc
        if (this.tryRelease(var1)) {
            AbstractQueuedSynchronizer.Node var2 = this.head;
            if (var2 != null && var2.waitStatus != 0) {
            	//唤醒后继节点
                this.unparkSuccessor(var2);
            }

            return true;
        } else {
            return false;
        }
    }
    

    private void unparkSuccessor(AbstractQueuedSynchronizer.Node var1) {
        int var2 = var1.waitStatus;
        if (var2 < 0) {
            compareAndSetWaitStatus(var1, var2, 0);
        }

        AbstractQueuedSynchronizer.Node var3 = var1.next;
        if (var3 == null || var3.waitStatus > 0) {
            var3 = null;
			//注意这部分,采用尾部遍历法找到离head节点最近且waitStatus<=0的节点并唤醒,信号量为1代表异常中断引起不会在执行后续逻辑
			//为什么采用尾部遍历法、这就和上面enq()方法相关,节点入队时可能多线程切换导致next并不是连续的,而前驱节点都是连续的
            for(AbstractQueuedSynchronizer.Node var4 = this.tail; var4 != null && var4 != var1; var4 = var4.prev) {
                if (var4.waitStatus <= 0) {
                    var3 = var4;
                }
            }
        }

        if (var3 != null) {
            LockSupport.unpark(var3.thread);
        }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值