ReentrantLock学习之公平锁过程

ReentrantLock支持公平锁和非公平锁,默认使用非公平锁。
在这里插入图片描述
主要记录公平锁里面的实现
在这里插入图片描述
通过源码可以发现公平锁的加锁方法

模拟ReentrantLock的加锁过程。
自定义一个Lock进行实现

public interface Lock {

    void  lock();

    void  unLock();
}

在ReentrantLock实现类里面,有几个变量

    /**
     * 0 表示未加锁状态
     * > 0 表示当前lock是加锁状态
     */
 private int state;
    /**
     * 独占模式
     * 同一时刻只有一个县城可以持有锁,其他的线程,在为获取到锁时,会被阻塞
     */
 private Thread exclusiveOwnerThread;

Node封装节点,里面包含有前置节点和引用节点和线程信息

    static final  class Node{
        // 前置节点引用
        Node prev;
        // 后置节点引用
        Node next;
        // 封装的线程本尊
        Thread thread;

        public Node(Thread thread) {
            this.thread = thread;
        }

        public Node() {
        }
    }

在这里插入图片描述
在源码中,加公平锁的时候,主要是进行该段代码逻辑获取加锁

    private void  acquireQueued(Node node,int arg){
        // 只有当前node成功获取到锁以后, 才会跳出自旋
        for (;;){
            //
            // 1. 当前node 是head的后继节点. 才有这个权限
            Node pred = node.prev;
            // 当前node 拥有抢占权限
            //
            if(pred == head  && tryAcquire(arg)){
                // 这里面, 说明当前线程 竞争锁成功啦。
                // 1.设置当前head 为当前线程的node

                // 2. 协助原始head 出队
                setHead(node);
                pred.next = null ; // help GC
                return;
            }
            // 将当前线程挂起
            LockSupport.park();

        }
    }
    private Node addWaiter(){
        Node newNode = new Node(Thread.currentThread());

        // 如何入队
        // 1. 找到newNode的前置节点
        // 2.更新newNode.prev = 前置节点
        // 3. CAS更新tail为 newNode
        // 4.更新pred.next = newNode

        // 前置条件: 队列已经有等待者node了,当前node 不是第一个进入对的node
        Node pred = tail;
        if(pred != null){
            newNode.prev = pred;
            // 当前线程成功入队
            if(compareAndSetTail(pred,newNode)){
                pred.next = newNode;

                return  newNode;
            }
        }

        // 1.tail == null 队列时空对象
        // 2.cas设置失败了,被其他线程抢先一步了。

        enq(newNode);
        return  newNode;
    }

    /**
     * 自旋入队,只有成功后才返回
     *
     *  1.tail == null 队列时空对象
     *  2.cas设置失败了,被其他线程抢先一步了。
     */
    private void enq(Node node){
        for(;;){
            // 第一种情况: 队列时空
            // 当前线程是第一个去抢占锁失败的线程
            // head节点 任何时候 都代表当前占用锁的线程
            if(tail == null){
                // 说明当前线程 给当前持有锁的线程 补充 head 操作成功
                if(compareAndSetHead(new Node())){
                    //
                    tail = head;
                    // 还会继续自旋

                }else {
                    // 说明当前队列中已经有node了,这里是一个追加node的过程
                    Node pred = tail;
                    if(pred != null){
                        node.prev = pred;
                        // 当前线程成功入队
                        if(compareAndSetTail(pred,node)){
                            pred.next = node;
                           return ;
                        }
                    }
                }
            }
        }
    }

在公平锁加锁的过程中,还有一段逻辑是获取锁的过程

  private boolean tryAcquire(int arg){
        if(state == 0){
            // 条件1: 取反后值为true,表示当前线程前面没有等待者
            // 条件2: lock方法可能有多线程调用,使用CAS,成立表示当前线程抢锁成功
            if(!hasQueuedPredecessor() && compareAndSetState(0,arg)){
                // 抢锁成功
                // 1.需要将exclusiveOwnerThread 设置为当前进入的线程
                this.exclusiveOwnerThread = Thread.currentThread();
                return true;
            }


            // 当前锁被占用的时候来到此处
            // 并且更新state值
        }else if(Thread.currentThread() == this.exclusiveOwnerThread) {
            // 里面不存在并发
            // 说明当前线程为持锁线程, 需要返回true,
           int c = getState();
           c = c + arg;
           // 越界判断
           this.state = c;
           return  true;
        }

        // 什么时候返回false?
        // 1.CAS加锁失败
        // 2.state > 0 且 当前线程不是占用者线程
        return  false;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

virtuousOne

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值