呀?这就是锁(四)?

java常用锁

AQS分析

我们从一个小程序入手,使用debug模式执行,代码如下:

package AboutSysn;

import java.util.concurrent.locks.ReentrantLock;

public class AQSDemo {
	static ReentrantLock reeLock = new ReentrantLock();
	static void m1() {
		reeLock.lock();
		System.out.println("AQSDemo");
		reeLock.unlock();
	}
	public static void main(String[] args) {
		m1();
	}
}

可以得到下面的结果

![未命名文件 (1)](C:\Users\saiji\Desktop\未命名文件 (1).png)

其中黄色标记的部分是AQS的重点

1.执行m1方法,调用ReentrantLock中 lock 方法,其内容如下:

​ 为方便我们下面的分析,我们将需要扩展说的方法都起上别名,别名以 M 开头

  public void lock() {
      	//这里的sync是我们创建的ReentrantLock对象,
        sync.lock();
    }
//因为我们没有使用公平锁(不交接公平锁的可以看《呀?,这就是锁(一)?》),所以
final void lock() {
    //这里就是CAS算法,调用的是 AbstractQueuedSynchronizer 类里面的方法,参数的意义是将0改成1
    if (compareAndSetState(0, 1))				// M1
        //这里是设置将当前线程设置为独占模式
        setExclusiveOwnerThread(Thread.currentThread());  //M2
    else
        //如果上面获取锁资源失败,进行下面的逻辑判断
        acquire(1);								//M3
    }

2.在1 中的Lock函数中又调用了其他的函数,我们继续向深处看

//M01调用了 AbstractQueuedSynchronizer 类里的 compareAndSetState 方法,这个方法又调用了unsafe 类的compareAndSwapInt 方法,这个地方就不用再往里面看了,已经不是java语法了,那么从这里地方我们可以分析出什么呢?观察参数,
	this -- 当前对象
    stateoffset -- 不知道
    expect -- 期望数
    update -- 更新数
        
protected final boolean compareAndSetState(int expect, int update) {
        // See below for intrinsics setup to support this
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
    }
//上面的参数中只有stateoffset我们不清楚是什么,那么点进去看一下
private static final long stateOffset;
static {
        try {
            stateOffset = unsafe.objectFieldOffset
                (AbstractQueuedSynchronizer.class.getDeclaredField("state"));
        } catch (Exception ex) { throw new Error(ex); }
    }
// 不难发现,这个变量是从 AbstractQueuedSynchronizer 中获取的成员变量,那么我们进入到AbstractQueuedSynchronizer 看看 state 是什么?
    private volatile int state;
//是一个被 volatile 修饰的 int 变量值,valatile的作用我们也很清楚,主要作用是提供了线程可见性可内存屏障,那么这个变量的作用应该就是不同线程之间通信使用,这样我们就能理解上面 compareAndSetState 函数的作用了,compareAndSetState的作用就是将 state 的值修改成自己想要的结果,同时记录修改线程(this参数的作用),如果这个操作成功了,表明锁资源已经被当前线程获取,即加锁成功

3.在2中我们分析M01的执行过程,但是W01执行有两种结果呀?M01返回值不同都做了哪些操作呢?

  1. //M1返回true会执行下面这个函数
    protected final void setExclusiveOwnerThread(Thread thread) {
            exclusiveOwnerThread = thread;
        }
    //将独占线程指向当前线程,即当前线程拥有资源
    
  2. //M1返回false会执行 AbstractQueuedSynchronizer 中的函数,参数是1
     public final void acquire(int arg) {
            if (!tryAcquire(arg) &&		//M4
                acquireQueued(addWaiter(Node.EXCLUSIVE), arg))   //M5
                selfInterrupt();			//M5
        }
    

4.下面是3.2中的M4方法内容

//这里时ReentrantLock中的方法
protected final boolean tryAcquire(int acquires) {
    return nonfairTryAcquire(acquires);
}

final boolean nonfairTryAcquire(int acquires) {
    //得到当前线程
    final Thread current = Thread.currentThread(); 
    //获取当前state的值
    int c = getState();
    //如果是0,证明没有线程锁定
    if (c == 0) {
        //尝试加锁,调用 unsafe.compareAndSwapInt(this, stateOffset, expect, update);
        if (compareAndSetState(0, acquires)) {
            //如果加锁成功,设置当前线程为独占线程
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    //如果不是0,证明存在线程锁定当前资源,判断是不是自己锁定的?
    else if (current == getExclusiveOwnerThread()) {
        //如果是自己锁定的,标识冲入,对 state+1
        int nextc = c + acquires;
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        //对state赋值
        setState(nextc);
        return true;
    }
    //没有获取到锁
    return false;
}

//m4方法为返回true的话 M5方法就不会继续执行,直接结束acquire,否则执行 M5,m5实现如下
acquireQueued(addWaiter(Node.EXCLUSIVE), arg) //M5

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不为空,证明队列中已经存在等待锁的线程Node,当前节点需要加到最后
    if (pred != null) {
        node.prev = pred;
        //同时是使用CAS
        if (compareAndSetTail(pred, node)) {
            pred.next = node;
            return node;
        }
    }
    //如果tail为空,证明没有等待队列,则新建一个
    enq(node);
    return node;
}
    
//将刚才的队列和state目标状态当做参数传入acquireQueued
final boolean acquireQueued(final Node node, int arg) {
    //是否取消锁争夺,如果过程中出现异常
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
            //拿到前一个Thread节点
            final Node p = node.predecessor();
            //如果前一个是头结点,并且自己获得了锁
            if (p == head && tryAcquire(arg)) {
                //把自己设置成头结点
                setHead(node);
                //把原来head指向自己的指向取消,此时还有自己指向前一个的指向
                p.next = null; // help GC
                //过程没有错误
                failed = false;
                //返回false ,结束M3
                return interrupted;
            }
            //如果上面的条件不成立,修改当前线程为可以parking 状态
            if (shouldParkAfterFailedAcquire(p, node) &&
                //park当前线程
                parkAndCheckInterrupt())
               	//如果被park了,就需要执行M5方法,唤醒这个线程
                interrupted = true;
        }
    } finally {
        //如果过程出现异常,取消队列中的这个Thread节点
        if (failed)
            cancelAcquire(node);
    }
}

这是第一版尝试去分析AQS源码,其中的不足支出欢迎指正,谢谢

posted @ 2021-05-11 16:40  二五树  阅读(45)  评论(0编辑  收藏  举报