1、AQS是什么

有协作功能的一些类,如ReentrantLock和Semaphore,CountDownLatch、ReentrantReadWriteLock,它们底层都实现了一个基类,这就是AQS

AQS可以理解为是一个工具类,帮助我们实现线程之间的协作

2、AQS演示


public class Semaphore implements java.io.Serializable {
    private static final long serialVersionUID = -3222578661600680210L;
    private final Sync sync;


    abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = 1192457210091910933L;

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

        final int getPermits() {
            return getState();
        }

        final int nonfairTryAcquireShared(int acquires) {
            for (;;) {
                int available = getState();
                int remaining = available - acquires;
                if (remaining < 0 ||
                    compareAndSetState(available, remaining))
                    return remaining;
            }
        }
}

 

都是一个Sync内部类,去继承我们的AbstractQueuedSynchronizer(AQS)类,我们其他的类也是这个套路

3、AQS原理

AQS由三个核心部分组成:

• State

• 控制线程强锁和配合的FIFO队列

• 期望协作工具类去实现的获取/释放等重要的方法

3.1、State

state的具体含义,会根据具体实现类的不同而不同,比如在Semaphore中,他表示“剩余的许可证的数量”,而在CountDownLatch中,它表示“还剩余倒数的数”

state是volatile修饰的,会被并发地修改,所以素有修改state的方法都需要保证线程安全,比如getState,setState已经compareAndSetState操作来读取和更新这个状态,这些方法都是依赖于atomic包的支持

//通过unsafe顶层指令来保证原子性

    protected final boolean compareAndSetState(int expect, int update) {

        // See below for intrinsics setup to support this

        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);

    }

在ReentrantLock中,state是用来表示锁的占有情况,包括可重入计数,当state的值为0的时候,标识该Lock不被任何线程锁占有

3.2、FIFO队列

这个队列是用来存放“等待的线程”,AQS就是“排队管理员”,当多个线程竞争锁的时候,只有一个线程可以获取,其他的线程串在一起进行等待。当锁释放时,锁管理器就会挑选一个合适的线程来占有刚刚释放的锁

ASQ会维护一个等待的线程队列,把线程都放到这个队列中,且这个队列是双向链表

3.3、获取/释放的方法

这个需要实现类自己实现,会依赖state的值,经常会阻塞(获取不到锁的时候)

3.4、AQS的实现类

1. 写一个类,想好协作的逻辑,实现获取/释放的方法

2. 内部写一个Sync类继承AbstractQueuedSynchronizer

3. 根据是否独占重写tryAcquirte/tryRelease或tryAcquireShared(int acquires)和tryReleaseShared(int releases)等方法,在之前写的获取/释放方法中调用AQS的acquire/release或者Shared方法

4、CountDownLatch源码分析

构造方法

//构造方法

    public CountDownLatch(int count) {

        if (count < 0) throw new IllegalArgumentException("count < 0");

        this.sync = new Sync(count);

    }

    //调用sync的构造

    Sync(int count) {

        etState(count);

    }

    //设置state值

    protected final void setState(int newState) {

        state = newState;

    }

getCount方法

//调用getCount方法

    public long getCount() {

        return sync.getCount();

    }

    int getCount() {

        return getState();

    }

    //最终返回的是state 的值

    protected final int getState() {

        return state;

    }

countDown方法

public void countDown() {

	sync.releaseShared(1);

}

public final boolean releaseShared(int arg) {

	if (tryReleaseShared(arg)) {

  //唤醒队列中的所有线程

  doReleaseShared();

  return true;

	}

	return false;

}

protected boolean tryReleaseShared(int releases) {

	//死循环

	for (;;) {

  //获取到state的值

  int c = getState();

  if (c == 0)

  	return false;

  //如果不为0,说明还可以减

  int nextc = c-1;

  //用原子操作保证线程安全

  if (compareAndSetState(c, nextc))

  	return nextc == 0;

	}

}

await方法

public void await() throws InterruptedException {

	sync.acquireSharedInterruptibly(1);

}

public final void acquireSharedInterruptibly(int arg)

  throws InterruptedException {

	if (Thread.interrupted())

  throw new InterruptedException();

	//判断state是不是0

	if (tryAcquireShared(arg) < 0)

  //进入到阻塞队列中

  doAcquireSharedInterruptibly(arg);

}

protected int tryAcquireShared(int acquires) {

	return (getState() == 0) ? 1 : -1;

}

• 调用CountDownLatch的await方法时,便会尝试获取“共享锁”,不过一开始是获取不到该锁的,于是线程被阻塞

• 而“共享锁”可获取到的条件,就是“锁计数器”的值为0

• 而“锁计数器”的初始值为count,每当一个线程调用该CountDownLatch对象的countDown()方法时,才将“锁计数器”-1

• count个线程调用countDown()之后,“锁计数器”才为0,而前面提到的等待获取线程的共享锁的线程才能继续运行

5、Semaphore源码分析

获取许可证

public void acquire(int permits) throws InterruptedException {

	//判断需要获取的许可证个数不能小于0,否则抛出异常

	if (permits < 0) throw new IllegalArgumentException();

	sync.acquireSharedInterruptibly(permits);

}


public final void acquireSharedInterruptibly(int arg)

  throws InterruptedException {

	if (Thread.interrupted())

  throw new InterruptedException();

	if (tryAcquireShared(arg) < 0)

  //小于0,就去等待

  doAcquireSharedInterruptibly(arg);

}

protected int tryAcquireShared(int acquires) {

	return nonfairTryAcquireShared(acquires);

}

//返回值如何小于0,获取许可证失败

final int nonfairTryAcquireShared(int acquires) {

	//死循环自旋

	for (;;) {

  //getState()获取许可证数量

  int available = getState();

  //判断当前许可证-获取许可证的值

  int remaining = available - acquires;

  if (remaining < 0 ||

  	//如果获取的值够,就会进行cas操作保证线程安全

  	compareAndSetState(available, remaining))

  	return remaining;

	}

}

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

• 看tryAcquire方法,判断nonfairTryAcquireShared大于等于0的话,代表获取成功

• 这里会先检查剩余的许可证数量够不够这次需要的,用减法来计算,如果不够,就返回负数,如果够了,就用cas操作自旋来改变state的状态,知道改变成功返回整数;期间如果被其他人修改了导致获取为负数,也会直接返回负数代表失败

释放许可证

public void release(int permits) {

	//判断释放的许可证,小于0抛出异常

	if (permits < 0) throw new IllegalArgumentException();

	sync.releaseShared(permits);

}

public void release(int permits) {

	if (permits < 0) throw new IllegalArgumentException();

	sync.releaseShared(permits);

}

protected final boolean tryReleaseShared(int releases) {

	//自旋

	for (;;) {

  int current = getState();

  int next = current + releases;

  //判断当前的state+需要释放的许可证不能大于最大允许数,大于就抛出异常

  if (next < current) // overflow

  	throw new Error("Maximum permit count exceeded");

  //使用cas操作,对state进行设置

  if (compareAndSetState(current, next))

  	return true;

	}

}

6、ReentrantLock源码分析

解锁

public void unlock() {

	sync.release(1);

}

public final boolean release(int arg) {

	//tryRelease()代表锁是否被释放了

	if (tryRelease(arg)) {

  Node h = head;

  if (h != null && h.waitStatus != 0)

  	//唤醒后面的节点

  	unparkSuccessor(h);

  return true;

	}

	return false;

}

protected final boolean tryRelease(int releases) {

	//getState()获取当前的次数,这个就是当前的次数减一

	int c = getState() - releases;

	//判断当前线程是不是持有锁的线程,不是就抛出异常

	if (Thread.currentThread() != getExclusiveOwnerThread())

  throw new IllegalMonitorStateException();

	boolean free = false;

	//当前次数减完一后,如果为0,就释放锁

	if (c == 0) {

  //free代表锁是不是自由的

  free = true;

  setExclusiveOwnerThread(null);

	}

	//不为0,则代表是重入过,只是state-1操作

	setState(c);

	return free;

}

• 由于可重入的,所以state代表重入的次数,每次释放锁,先判断是不是当前持有锁的线程释放的,如果不是抛出异常;如果是的,重入的次数减一,如果减到0,就说明完全释放了,于是free就是true,并且将state设置为0

加锁

final void lock() {

	if (compareAndSetState(0, 1))

  //将当前的线程设置为持有锁的线程

  setExclusiveOwnerThread(Thread.currentThread());

	else

  //

  acquire(1);

}

public final void acquire(int arg) {

	if (!tryAcquire(arg) &&

  //addWaiter()添加到队列中

  acquireQueued(addWaiter(Node.EXCLUSIVE), arg))

  selfInterrupt();

}

protected final boolean tryAcquire(int acquires) {

	final Thread current = Thread.currentThread();

	int c = getState();

	//等于0代表没有线程持有

	if (c == 0) {

  if (!hasQueuedPredecessors() &&

  	//通过cas操作来获取到这把锁

  	compareAndSetState(0, acquires)) {

  	setExclusiveOwnerThread(current);

  	return true;

  }

	}

	//我这个线程正好是持有这把锁,是一个重入的操作

	else if (current == getExclusiveOwnerThread()) {

  //重入的次数+1

  int nextc = c + acquires;

  //判断重入后的值是不是溢出

  if (nextc < 0)

  	throw new Error("Maximum lock count exceeded");

  //将state值设置进去

  setState(nextc);

  return true;

	}

	//代表锁被其他线程持有,直接返回false

	return false;

}

7、尝试利用AQS实现自己的Latch门闩

一次性门闩

public class OneShotLatch {


    private final Sync sync = new Sync();


    //获取

    public void await(){

        sync.acquireShared(0);

    }


    //释放

    public void signal(){

        sync.releaseShared(0);

    }



    //不需要强制重写,是因为不确定我们需要重写哪些方法

    static class Sync extends AbstractQueuedSynchronizer {

        @Override

        protected int tryAcquireShared(int arg) {

            return (getState() == 1 ) ? 1 : -1;

        }


        @Override

        protected boolean tryReleaseShared(int arg) {

            setState(1);


            return true;

        }

    }


    public static void main(String[] args) {

        final OneShotLatch oneShotLatch = new OneShotLatch();

        for (int i = 0; i < 10; i++) {

            new Thread(new Runnable() {

                public void run() {

                    System.out.println(Thread.currentThread().getName() + "尝试获取门闩,获取失败那就等待");

                    oneShotLatch.await();

                    System.out.println( "开闸放行" +Thread.currentThread().getName() + "继续运行");

                }

            }).start();


        }

        try {

            Thread.sleep(5000);

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

        oneShotLatch.signal();


        new Thread(new Runnable() {

            public void run() {

                System.out.println(Thread.currentThread().getName()+"尝试获取latch,获取失败那就等待");

                oneShotLatch.await();

                System.out.println("开闸放行"+Thread.currentThread().getName()+"继续运行");

            }

        }).start();

    }

}