多线程原理(一)--ReentrantLock实现原理

ReentrantLock实现原理

        ReentrantLock lock=new ReentrantLock(); 
        lock.lock();
        System.out.println("lock");
        lock.unlock();

1 ReentrantLock底层是基于AQS(AbstractQueuedSynchronizer)的。
AbstractQueuedSynchronizer包含的属性包括int成员变量state表示同步状态、一个node对象(pre,next,thread,waitStatus线程状态)、两个节点(头节点,尾节点)、当前持有锁的线程。

先从公平锁开始

1 当第一个线程t1执行lock.lock()方法时:
1.1会先调用acquire方法

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

1.2 tryAcquire方法中尝试加锁

        protected final boolean tryAcquire(int acquires) {
        //获取当前线程
            final Thread current = Thread.currentThread();
            //获取同步状态
            int c = getState();
            //判断同步状态是否为0,此时是第一个线程没有其他线程持有锁,所以c==0
            if (c == 0) {
              //hasQueuedPredecessors()会判断是否需要排队,此时第一个线程是不需要排队的
              //compareAndSetState时cas操作改变state的状态,
              //若改变成功把当前线程设置为持有锁的线程
                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.3上一步中的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;
        //这里队列还没有被初始化,所以h==t返回false
        return h != t &&
            ((s = h.next) == null || s.thread != Thread.currentThread());
    }

!hasQueuedPredecessors()则返回true,表示不需要排队,直接cas操作改变state的状态
cas成功后tryAcquire返回true,acquire中的!tryAcquire(arg)为false,此时直接退出acquire
2 当第二个线程t2进来时,
2.1此时调用tryAcquire方法会判断state==1,

        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;
                }
            }
           //第二个线程进来判断state==1
           //然后判断当前线程和持有锁的线程是不是同一个(可重入)
           //这里时第二个线程会直接返回false
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
    }

tryAcquire返回false,acquire中的!tryAcquire(arg)为true,
2.2此时直接进入到acquire中acquireQueued(addWaiter(Node.EXCLUSIVE), arg)),先调用
addWaiter(Node.EXCLUSIVE)

  private Node addWaiter(Node mode) {
        //根据当前线程来实例化一个node
        Node node = new Node(Thread.currentThread(), mode);
        // Try the fast path of enq; backup to full enq on failure
        Node pred = tail;
        //如果tail为空代表队列还没初始化
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        //此时队列还没初始化,直接进入enq方法
        enq(node);
        return node;
    }

2.3 上一步中的enq方法

  /**
   这个方法执行的操作就是当队列第一次初始化时
   会new一个空节点,然后把头结点指向new出来的这个空节点,尾节点指向上一步传过来的
    根据当前线程来实例化的node(这里就表示为t2)。
    至于为什么要new一个空节点出来,是为了表示正在持有锁的那个线程,当t2持有锁后会将t2设置为头节点,
    t2的thread设置为null
    
  */
    private Node enq(final Node node) {
        for (;;) {
            Node t = tail;
            if (t == null) { // Must initialize
                //第一次循环进来尾节点为null,cas将头指针指向new的空节点
                if (compareAndSetHead(new Node()))
                   //然后尾指针也指向头指针
                    tail = head;
            } else {
            //第二次循环进来尾节点不为null,下面就是插入t2,将尾指针指向t2
            //此时t2已经入队,不代表立即排队等待
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }

2.4 addWaiter返回t2,传给acquireQueued方法

    final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                //当t2入队后并不会立即park,而是自旋一次,看能不能拿到锁
                //会先判断自己上一个是不是head
                //(若自己前面还有线程在等待,就没必要尝试拿锁了,轮不到自己)
                //tryAcquire尝试获取锁,和前面一样
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                //依然拿不到锁,t2阻塞--park
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

3 当第三个线程t3来时
操作和t2一样,不同的是:
–addWaiter方法,根据当前线程来实例化一个node后,此时判断tail不为空,将node的pre指向t2,队尾指向t3,t2的next指向t3,就是把t3入队
–t3入队后就不会自旋了,因为他的上一个不是head,直接park睡眠

非公平锁情况

非公平锁会有两次抢锁过程
1 当线程进来直接cas尝试获取锁,
2 若失败会调用tryAcquire方法再次尝试加锁(判断锁的状态,若state为0直接cas获取锁,不会判断是否需要排队)

/**
和公平锁的区别在于,直接cas尝试获取锁,
*/
        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

若第一次抢锁失败tryAcquire调用的是以下方法

/**
和公平锁的区别在于,若state为0直接cas获取锁,不会判断是否需要排队

*/
        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;
        }
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值