【并发编程笔记】 ---- AQS内部原理解析及应用源码解析

目录

1. 什么是AQS?
2. AQS作用
3. AQS的重要性、地位
4. AQS内部原理解析(同步状态、同步队列、获取/释放方法)
5. AQS在CountDownLatch应用
6. AQS在Semaphore应用
7. AQS在ReentrantLock应用
8. AQS实现一个简单的线程协作器

1. 什么是AQS?

AQS ,AbstractQueuedSynchronizer ,即队列同步器。它是构建锁或者其他同步组件的基础框架(如 ReentrantLock、ReentrantReadWriteLock、Semaphore 等),J.U.C 并发包的作者(Doug Lea)期望它能够成为实现大部分同步需求的基础。它是 J.U.C 并发包中的核心基础组件

2. AQS作用

AQS是一个用于构建锁、同步器、协作工具类的工具类(框架),解决了在实现同步器时涉及当的大量细节问题,例如获取同步状态、FIFO 同步队列,有了AQS以后,更多的协作工具类都可以很方便的被写出来

在基于 AQS 构建的同步器中,只能在一个时刻发生阻塞,从而降低上下文切换的开销,提高了吞吐量。同时在设计 AQS 时充分考虑了可伸缩性,因此 J.U.C 中,所有基于 AQS 构建的同步器均可以获得这个优势。

3. AQS的重要性、地位

AbstractQueuedSynchronizer是Doug Lea写的,从JDK1.5加入的一个基于FIFO等待队列实现的一个用于实现同步器的基础框架

  • ReetrantLock的Sync锁、FairSync公平锁、NonFairSync非公平锁
  • ReetrantReadWriteLock的Sync锁、FairSync非公平锁、NonfairSync非公平锁
  • Semaphore的Sync锁、FairSync公平锁、NonFairSync非公平锁
  • CountDownLatch的Sync锁
  • ThreadPoolExecutor的Worker

JUC包里面几乎所有的有关锁、多线程并发以及线程同步器等组件的实现都是基于AQS这个框架
【并发编程笔记】 ---- AQS内部原理解析及应用源码解析_第1张图片

4. AQS内部原理解析(同步状态、同步队列、获取/释放方法)

AQS最核心的三大部分: state、控制线程抢锁和配合的FIFO队列、期望协作工具类去实现的获取/释放等重要方法

4.1 同步状态

AQS的主要使用方式是继承,子类通过继承同步器,并实现它的抽象方法来管理同步状态
AQS使用一个int类型的成员变量state来表示同步状态

  • state > 0时,表示已经获取了锁
  • state = 0时,表示释放了锁

它提供了三个方法,来对同步状态state进行操作,并且AQS可以确保对state的操作是安全的:

  • #getState()
  • #setState(int newState)
  • #compareAndSetState(int expect, int update)

state的具体含义会根据具体实现类的不同而不同,比如在Semaphore里,它表示"剩余的许可证的数量",而在CountDownLatch里,它表示"还需要倒数的数量",在ReentrantLock中,它表示"锁"的占有情况,包括可重入计数

4.2 同步队列

AQS通过内置的FIFO同步队列(双向队列)来完成资源获取线程的排队工作:

  • 如果当前线程获取同步状态失败(锁)时,AQS则会将当前线程以及等待状态等信息构造成一个节点(Node)并将其加入同步队列,同时会阻塞当前线程
  • 当同步状态释放时,则会把节点中的线程唤醒,使其再次尝试获取同步状态
    【并发编程笔记】 ---- AQS内部原理解析及应用源码解析_第2张图片

4.3 获取/释放方法

  • 获取方法

    • 获取操作会依赖state变量,经常会阻塞(比如获取不到的时候)

    • #acquire(int arg):独占式获取同步状态。如果当前线程获取同步状态成功,则由该方法返回;否则,将会进入同步队列等待。该方法将会调用可重写的 #tryAcquire(int arg) 方法

    • #acquireShared(int arg):共享式获取同步状态,如果当前线程未获取到同步状态,将会进入同步队列等待,与独占式的主要区别是在同一时刻可以有多个线程获取到同步状态

      在Semaphore中,获取就是acquire方法,作用是获取一个许可证
      在CountDownLatch中,获取就是await方法,作用是"等待,直到倒数结束"

  • 释放方法

    • 释放操作不会阻塞

    • #release(int arg):独占式释放同步状态,该方法会在释放同步状态之后,将同步队列中第一个节点包含的线程唤醒

    • #releaseShared(int arg):共享式释放同步状态

      在Semaphore中,释放就是release方法,作用是释放一个许可证
      在CountDownLatch中,释放就是countDown方法,作用是"倒数1个数"

  • 需要重写tryAcquire和tryRelease等方法

    • 需要调用继承了AQS的Sync类的acquire和release方法
    • #tryAcquire(int arg):独占式获取同步状态,获取同步状态成功后,其他线程需要等待该线程释放同步状态才能获取同步状态。
    • #tryRelease(int arg):独占式释放同步状态。
    • #tryAcquireShared(int arg):共享式获取同步状态,返回值大于等于 0 ,则表示获取成功;否则,获取失败。
    • #tryReleaseShared(int arg):共享式释放同步状态。

5. AQS在CountDownLatch应用

5.1 构造函数

//构造一个用给定计数初始化的 CountDownLatch 
public CountDownLatch(int count) {
        if (count < 0) throw new IllegalArgumentException("count < 0");
        this.sync = new Sync(count);
}

Sync(int count) {
            setState(count);
}

protected final void setState(int newState) {
        state = newState;
}

5.2 await方法
CountDownLatch 提供 #await() 方法,来使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断

// CountDownLatch.java
public void await() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
}
// AQS.java 该方法内部使用 AQS 的 #acquireSharedInterruptibly(int arg) 方法
public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        if (tryAcquireShared(arg) < 0)
            doAcquireSharedInterruptibly(arg);
}
//在内部类 Sync 中重写了 #tryAcquireShared(int arg)方法
protected int tryAcquireShared(int acquires) {
		//getState() 方法,获取同步状态,其值等于计数器的值
		// 等于0则表示倒计数为0,放行所有等待的线程,否则,则挂起当前线程
        return (getState() == 0) ? 1 : -1;
}
// AQS.java 该方法为一个自旋方法会尝试一直去获取同步状态,挂起当前线程
private void doAcquireSharedInterruptibly(int arg)
        throws InterruptedException {
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
            for (;;) {
                final Node p = node.predecessor();
                if (p == head) {
                    int r = tryAcquireShared(arg);
                    if (r >= 0) {
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        failed = false;
                        return;
                    }
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

5.3 countDown

CountDownLatch 提供 #countDown() 方法,递减锁存器的计数。如果计数到达零,则唤醒所有等待的线程

// CountDownLatch.java
public void countDown() {
        sync.releaseShared(1);
}
// 内部调用 AQS 的 #releaseShared(int arg) 方法,来释放共享锁同步状态
public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
            // 如果释放成功将会释放之前阻塞的线程 unparkSuccessor 
            doReleaseShared();
            return true;
        }
        return false;
}
// 被CountDownLatch内部类Sync重写了AQS的共享释放方法
protected boolean tryReleaseShared(int releases) {
      // Decrement count; signal when transition to zero
         for (;;) {
         	 // 获取锁状态
             int c = getState();
             // 当state=0时候,说明之前已经释放过了,本次就不需要重复释放了
             if (c == 0)
                 return false;
             // 计算新“锁计数器”
             int nextc = c-1;
             // 用cas更新锁状态(计数器)
             if (compareAndSetState(c, nextc))
                 return nextc == 0;
         }
 }

6. AQS在Semaphore应用

在Semaphore中,state表示许可证的剩余数量

6.1 acquire方法

//Semaphore.java
public void acquire() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
}
// AQS.java
public final void acquireSharedInterruptibly(int arg) throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        if (tryAcquireShared(arg) < 0)
        	// 阻塞等待,信号量不足时阻塞线程
            doAcquireSharedInterruptibly(arg);
}
// Semaphore.java
protected int tryAcquireShared(int acquires) {
        return nonfairTryAcquireShared(acquires);
}

// Semaphore.java
final int nonfairTryAcquireShared(int acquires) {
            for (;;) {
            	//获取当前的信号量许可
                int available = getState();
                // 设置“获得acquires个信号量许可之后,剩余的信号量许可数”
                int remaining = available - acquires;
                //  CAS设置信号量
                if (remaining < 0 || compareAndSetState(available, remaining))
                    return remaining;
            }
}

6.2 release方法

// Semaphore.java
public void release(int permits) {
        if (permits < 0) throw new IllegalArgumentException();
        sync.releaseShared(permits);
}
// AQS.java
public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
        //可唤醒阻塞等待 Semaphore 的许可的线程
            doReleaseShared();
            return true;
        }
        return false;
}
// Semaphore.java
protected final boolean tryReleaseShared(int releases) {
     	for (;;) {
             int current = getState();
             //信号量的许可数 = 当前信号许可数 + 待释放的信号许可数
             int next = current + releases;
             if (next < current) // overflow
                 throw new Error("Maximum permit count exceeded");
             //设置可获取的信号许可数为next
             if (compareAndSetState(current, next))
                 return true;
         }
 }

7. AQS在ReentrantLock应用

lock方法

// ReentrantLock.java
public void lock() {
        sync.lock();
}

// ReentrantLock.java
final void lock() {
//#lock() 实现方法,首先基于 AQS state 进行 CAS 操作,将 0 => 1 。
//若成功,则获取锁成功。若失败,执行 AQS 的正常的同步状态获取逻辑
       if (compareAndSetState(0, 1))
            setExclusiveOwnerThread(Thread.currentThread());
       else
            acquire(1);
}
// AQS.java
public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
 }
 // ReentrantLock.java
 protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
 }
 // ReentrantLock.java
 final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            //获取同步状态
            int c = getState();
            //state == 0,表示没有该锁处于空闲状态
            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;
        }

#nonfairTryAcquire方法主要逻辑:首先判断同步状态 state == 0 ?

  • 如果是,表示该锁还没有被线程持有,直接通过CAS获取同步状态。
    • 如果成功,返回 true 。
    • 否则,返回 false 。
  • 如果不是,则判断当前线程是否为获取锁的线程?
    • 如果是,则获取锁,成功返回 true 。成功获取锁的线程,再次获取锁,这是增加了同步状态 state
    • 否则,返回 false 。

unlock

// ReentrantLock.java
public void unlock() {
        sync.release(1);
}
// AQS.java
public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
 }
 // ReentrantLock.java
protected final boolean tryRelease(int releases) {
        int c = getState() - releases;
       // 如果释放的不是持有锁的线程,抛出异常
          if (Thread.currentThread() != getExclusiveOwnerThread())
              throw new IllegalMonitorStateException();
          boolean free = false;
          // state == 0 表示已经释放完全了,其他线程可以获取同步状态了
          if (c == 0) {
              free = true;
              setExclusiveOwnerThread(null);
          }
          setState(c);
          return free;
}

8. AQS实现一个简单的线程协作器

public class OneShotLatch {

	private final Sync sync = new Sync();

	public void signal(){
		sync.releaseShared(0);
	}

	public void await(){
		sync.acquireShared(0);
	}

	private class Sync extends AbstractQueuedSynchronizer {

		@Override
		protected int tryAcquireShared(int arg) {
			// 是否开闸,如果是返回1, 否则返回-1<0进入等待队列
			return getState() == 1 ? 1 : -1;
		}

		@Override
		protected boolean tryReleaseShared(int arg) {
			// state = 1 表示开闸, state = 0表示关闸
			setState(1);
			return true;
		}

	}


	public static void main(String[] args) throws InterruptedException {
		OneShotLatch oneShotLatch = new OneShotLatch();
		for (int i = 0; i < 10; i++) {
			new Thread(new Runnable() {
				@Override
				public void run() {
					System.out.println(Thread.currentThread().getName() + "尝试获取latch,获取失败就等待");
					oneShotLatch.await();
					System.out.println(Thread.currentThread().getName() + "开闸放行,继续运行");
				}

			}).start();
		}

		Thread.sleep(4000);
		oneShotLatch.signal();


	}
}

【并发编程笔记】 ---- AQS内部原理解析及应用源码解析_第3张图片

参考:

  • 芋道源码
  • 慕课网悟空老师课程

你可能感兴趣的:(并发)