AQS之ReentrantLock详解

本文深入解析了Java并发库中的ReentrantLock实现。从lock()方法开始,详细介绍了如何通过CAS尝试获取锁,如果获取失败则进入等待队列。在unlock()方法中,解释了如何释放锁,并唤醒等待队列中的下一个节点。文章通过代码分析展示了ReentrantLock的非公平锁策略及其内部节点管理机制。
摘要由CSDN通过智能技术生成

我们写一个简单实例

public class AQSTest {

    static ReentrantLock lock = new ReentrantLock();


    public static void main(String[] args) {
        Thread thread1 = new Thread() {
            @Override
            public void run() {
                try {
                    lock.lock();
                    System.out.println("thread1 拿锁");
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    System.out.println("thread1 解锁");
                    lock.unlock();
                }
            }
        };

        thread1.setName("Thread1");

        Thread thread2 = new Thread() {
            @Override
            public void run() {
                lock.lock();
                System.out.println("thread2 拿锁");
                for (int i = 0; i < 1000; i++) {

                }
                System.out.println("thread2 解锁");
                lock.unlock();
            }
        };

        thread2.setName("Thread2");
        thread1.start();
        thread2.start();
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

lock方法进入ReetrantLock的lock方法

final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

1、我们发现此时我们将当前ReetrantLock对象的状态通过原子性操作CAS尝试修改(拿锁)并且设置当前的线程为currentThread(),

2、如果拿不到就进入acquire方法

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

3、我们将这个代码分成2步来看

!tryAcquire(arg)
    |
    |
   \|/

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) {
                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;
        }    

    |
    |
   \|/
        
        
解析: 首先拿到当前线程,然后拿到当前锁的状态,
	 判断一下,
		如果c==0也就是无锁,我们就尝试用CAS将其设置为acquires,而这个值我们传的是1,也就是CAS修改状态(拿锁),并且返回true。
            
        如果当前线程指向的是已经获取锁的线程,表示再次上锁,+1,
			如果nextc<0,抛出异常,

			重新设置锁状态,返回true     
                
            否则就是无法获取锁,返回false
                
                
    |
    |
   \|/
                
                
        

 第二段代码

acquireQueued(addWaiter(Node.EXCLUSIVE), arg)
    
    |
    |
   \|/
解析:首先我们判断非公平锁方式是否可以拿到锁,因为 !tryAcquire(arg) --> 拿不到就是true,继续执行,拿到立刻返回
    
    |
    |
   \|/
    
    private Node addWaiter(Node mode) {
        Node node = new Node(Thread.currentThread(), mode);
        // Try the fast path of enq; backup to full enq on failure
        Node pred = tail;
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        enq(node);
        return node;
    }
    
解析:首先我们看传入参数Node.EXCLUSIVE --> static final Node EXCLUSIVE = null;
	这个源代码写的是我们传入了一个空的Node对象
    
        首先我们创建了一个当前线程的node对象, 

		Node(Thread thread, Node mode) {     // Used by addWaiter
            this.nextWaiter = mode;
            this.thread = thread;
        }

		构造器告诉我们传入的是后一个等待者的node,

		将尾部tail--Node对象赋给pred,
		如果前一个Node结点对象不为空,那么当前线程创建的node结点的前一个Node结点赋值tail(加入队列尾部),然后CAS修改前一个node结点的后继结点为当前
        线程的node结点.
        
        然后我们进入enq方法,


		|
        |
       \|/
            
       private Node enq(final Node node) {
        for (;;) {
            Node t = tail;
            if (t == null) { // Must initialize
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }     

	传入的是当前线程的node结点,这里使用的是一个空转,
	反复尝试将当前node结点插入队列,
	如果前一个结点为空(也就是队列未初始化),则通过CAS新建一个头node结点插入
	否则,队列已经初始化好了,那么,我们将当前node结点进行尾插入队列
        
        
        
        |
        |
       \|/
        
        
        final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
    

		|
        |
       \|/
            
解析: 首先传入当前node结点,arg = 1, 创建一个failed失败标记,进入try, predecessor()方法拿到前一个结点,
	 如果p(前结点) == 头结点,尝试给当前线程node结点加锁,
		
		成功:当前结点前移变成头结点,删除原头结点p,修改failed失败标记为false表示加锁成功,返回中断,

 unlock方法

public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }




解析:首先他调用了tryRelease方法,arg传参为1,
	
		|
        |
       \|/
            
            
   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;
        }         


		|
        |
       \|/
            
            
解析:首先传入releases参数1,拿到当前锁的状态,如果当前锁持有对象不为对应的释放锁的对象,报出非法monitor状态错误,
	
	如果是当前线程,则创建free标记来记录是否释放成功,此时如果c的状态为0则释放锁,并且修改锁持有线程对象为null,

	最后返回free
        
        
    	|
        |
       \|/
        
        
  public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }



解析:我们回到release方法,如果没有释放锁成功,直接返回false,
	如果释放锁成功,获取当前队列的头node结点,如果头结点不为null,并且等待状态不为0,我们就让他苏醒,调用unparkSuccessor
        
             
    	|
        |
       \|/
        
        private void unparkSuccessor(Node node) {
        /*
         * If status is negative (i.e., possibly needing signal) try
         * to clear in anticipation of signalling.  It is OK if this
         * fails or if status is changed by waiting thread.
         */
        int ws = node.waitStatus;
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);

        /*
         * Thread to unpark is held in successor, which is normally
         * just the next node.  But if cancelled or apparently null,
         * traverse backwards from tail to find the actual
         * non-cancelled successor.
         */
        Node s = node.next;
        if (s == null || s.waitStatus > 0) {
            s = null;
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
        if (s != null)
            LockSupport.unpark(s.thread);
    }

		|
        |
       \|/
            
解析:首先拿到当前头结点的等待状态,如果状态小于0我们尝试CAS修改node结点(注意是node,不是ws)其状态为0,      
	如果头结点后面不为空,我们调用unpark方法解锁睡眠对象,这个时候,整个unlock方法就执行完了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值