• 呀?这就是锁(四)?


    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:UserssaijiDesktop未命名文件 (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源码,其中的不足支出欢迎指正,谢谢

  • 相关阅读:
    web-9. 动态网页与数据库-2
    web-9. 动态网页与数据库
    web-8. 多框架页面的创建
    web-7. 丰富页面的多媒体
    web-6. 组织页面的表格
    yocto术语二
    yocto术语
    linux source
    linux 添加环境变量
    ubuntu上网
  • 原文地址:https://www.cnblogs.com/aierben/p/14756108.html
Copyright © 2020-2023  润新知