AQS 与 CAS

一 . CAS

CAS(compare and swap),是解决多线程情况下锁性能损耗的机制。在java中,sun.misc.Unsafe 类提供了硬件级别的原子操作来实现CAS。

二. CAS典型应用

java.util.concurrent.atomic 包下的类大多是使用CAS操作来实现的:AtomicInteger

public class AtomicInteger extends Number implements java.io.Serializable {
    private static final long serialVersionUID = 6214790243416807050L;

    // setup to use Unsafe.compareAndSwapInt for updates
    private static final Unsafe unsafe = Unsafe.getUnsafe();

    private volatile int value;// 初始int大小
    // 省略了部分代码...

    // 带参数构造函数,可设置初始int大小
    public AtomicInteger(int initialValue) {
        value = initialValue;
    }
    // 不带参数构造函数,初始int大小为0
    public AtomicInteger() {
    }

    // 获取当前值
    public final int get() {
        return value;
    }

    // 设置值为 newValue
    public final void set(int newValue) {
        value = newValue;
    }

    //返回旧值,并设置新值为 newValue
    public final int getAndSet(int newValue) {
        /**
        * 这里使用for循环不断通过CAS操作来设置新值
        * CAS实现和加锁实现的关系有点类似乐观锁和悲观锁的关系
        * */
        for (;;) {
            int current = get();
            if (compareAndSet(current, newValue))
                return current;
        }
    }

    // 原子的设置新值为update, expect为期望的当前的值
    public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

    // 获取当前值current,并设置新值为current+1
    public final int getAndIncrement() {
        for (;;) {
            int current = get();
            int next = current + 1;
            if (compareAndSet(current, next))
                return current;
        }
    }

    // 此处省略部分代码,余下的代码大致实现原理都是类似的
}

在竞争不是非常激烈的情况下,该包下的原子操作性能比使用 synchronized 关键字的方式高效的多。

三. AQS

AQS 是Java.util.concurrent包的核心类。
AQS 与 CAS_第1张图片
整个包中很多类的结构都是如此,比如Semaphore,CountDownLatch都有一个内部类Sync,而所有的Sync都是继承自AbstractQueuedSynchronizer。 所以说想要读懂Java并发包的代码,首先得读懂这个类。
AQS的核心是通过一个共享变量来同步状态,变量的状态由子类去维护。AQS所做的工作主要有两部分:
(1)线程阻塞队列的维护
(2)线程的阻塞与唤醒
共享变量的修改都是通过Unsafe类提供的CAS操作完成的。AbstractQueuedSynchronizer类的主要方法是acquire和release,它们是典型的模板方法。
acquire方法用来获取锁,返回true说明线程获取成功继续执行,一旦返回false则线程加入到等待队列中,等待被唤醒,release方法用来释放锁。 下面这4个方法由子类去实现

//独占模式获取
protected boolean tryAcquire(int arg)
//独占模式释放
protected boolean tryRelease(int arg)
//共享模式获取
protected int tryAcquireShared(int arg)
//共享模式释放
protected boolean tryReleaseShared(int arg)

四. AQS的用法

下面的SimpleLock类实现了一个最简单非重入的互斥锁的功能。

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();
} 

五. AQS源码分析

http://zhanjindong.com/2015/03/10/java-concurrent-package-aqs-overview

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