Fork me on GitHub

Java并发包源码学习之AQS框架(一)概述

AQS其实就是java.util.concurrent.locks.AbstractQueuedSynchronizer这个类。 阅读Java的并发包源码你会发现这个类是整个java.util.concurrent的核心之一,也可以说是阅读整个并发包源码的一个突破口。

比如读ReentrantLock的源码你会发现其核心是它的一个内部类Sync:

aqs-overview.png

 

整个包中很多类的结构都是如此,比如Semaphore,CountDownLatch都有一个内部类Sync,而所有的Sync都是继承自AbstractQueuedSynchronizer。 所以说想要读懂Java并发包的代码,首先得读懂这个类。

AQS简核心是通过一个共享变量来同步状态,变量的状态由子类去维护,而AQS框架做的是:

  • 线程阻塞队列的维护
  • 线程阻塞和唤醒

共享变量的修改都是通过Unsafe类提供的CAS操作完成的。AbstractQueuedSynchronizer类的主要方法是acquirerelease,典型的模板方法, 下面这4个方法由子类去实现:

protected boolean tryAcquire(int arg)
protected boolean tryRelease(int arg)
protected int tryAcquireShared(int arg)
protected boolean tryReleaseShared(int arg)

acquire方法用来获取锁,返回true说明线程获取成功继续执行,一旦返回false则线程加入到等待队列中,等待被唤醒,release方法用来释放锁。 一般来说实现的时候这两个方法被封装为lockunlock方法。

下面的SimpleLock类实现了一个最简单非重入的互斥锁的功能,实际上它就是ThreadPoolExecutor$Worker的实现(以后的文章会提到这个类)。

class SimpleLock extends AbstractQueuedSynchronizer {
    private static final long serialVersionUID = -7316320116933187634L;

    public SimpleLock() {

    }

    protected boolean tryAcquire(int unused) {
        if (compareAndSetState(0, 1)) {
            setExclusiveOwnerThread(Thread.currentThread());
            return true;
        }
        return false;
    }

    protected boolean tryRelease(int unused) {
        setExclusiveOwnerThread(null);
        setState(0);
        return true;
    }

    public void lock() {
        acquire(1);
    }

    public boolean tryLock() {
        return tryAcquire(1);
    }

    public void unlock() {
        release(1);
    }

    public boolean isLocked() {
        return isHeldExclusively();
    }
}
public static void main(String[] args) throws InterruptedException {
    final SimpleLock lock = new SimpleLock();
    lock.lock();

    for (int i = 0; i < 10; i++) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                lock.lock();
                System.out.println(Thread.currentThread().getId() + " acquired the lock!");
                lock.unlock();
            }
        }).start();
        // 简单的让线程按照for循环的顺序阻塞在lock上
        Thread.sleep(100);
    }

    System.out.println("main thread unlock!");
    lock.unlock();
}

运行上面的测试代码,结果如下:

main thread unlock!
9 acquired the lock!
10 acquired the lock!
11 acquired the lock!
12 acquired the lock!
13 acquired the lock!
14 acquired the lock!
15 acquired the lock!
16 acquired the lock!
17 acquired the lock!
18 acquired the lock!

会发现等待的线程是按照阻塞时的顺序依次获取到锁的。 这是因为AQS是基于一个叫CLH lock queue的一个变种来实现线程阻塞队列的,我们下一篇文章就来简单了解下CLH lock queue。

后续文章计划如下:

  • 《Java并发包源码学习之AQS框架(二)CLH lock queue和自旋锁》
  • 《Java并发包源码学习之AQS框架(三)LockSupport》
  • 《Java并发包源码学习之AQS框架(四)AbstractQueuedSynchronizer源码分析》
  • 《Java并发包源码学习之AQS框架(五)ConditionObject源码分析》

  ……

  • 《Java并发包源码学习之锁(一)概述》
  • 《Java并发包源码学习之锁(二)ReentrantLock源码分析》

  ……

  • 《Java并发包源码学习之线程池(一)概述》
  • 《Java并发包源码学习之线程池(二)ThreadPoolExecutor源码分析》

  ……

学习Java并发包源码的初衷是为了搞清之前遇到的一个问题,其实很早之前就打算看这块的源码但一直没看下去,所以说 看源码一定要有目的不能为了看而看。

 

posted @ 2015-03-10 19:39  zhanjindong  阅读(8858)  评论(1编辑  收藏  举报
TOP