Java多线程并发器之AbstractQueuedSynchronizer分析

AbstractQueuedSynchronizer

AbstractQueuedSynchronizer是Java并发工具包中最重要的工具,它是一个抽象类,为Java的各种同步器,锁等提供了并发抽象,是由大名鼎鼎的Doug Lea完成。

java.util.concurrent提供了很多并发工具类,其中很多都是基于AbstractQueuedSynchronizer实现的。如,ReentrantLock、ReentrantReadWriteLock、CountDownLatch、CyclicBarrier【CyclicBarrier内部使用ReentrantLock和Condition实现,间接使用了AbstractQueuedSynchronizer】、Semaphore、各种BlockingQueue【与CyclicBarrier类似,内部使用了ReentrantLock】、ConcurrentHashMap【与CyclicBarrier类似,内部使用了ReentrantLock】、CopyOnWriteArrayList【与CyclicBarrier类似,内部使用了ReentrantLock】等。由此可以看出AbstractQueuedSynchronizer的重要性。

AbstractQueuedSynchronizer内部使用了compareAndSwap(CAS)无锁操作来实现锁的,compareAndSwap()是使用native方法实现的,其提供了一个原子语义上的操作,即要更新一个变量的值时,如果该变量的原始值是我认为的原始值,那么我就更新成功,否则就更新失败,更新失败就一直尝试更新,若干次之后总会更新成功。而不是像synchronized那样从操作系统层面上加锁,因此引入AbstractQueuedSynchronizer之后大大优化了Java的锁性能。

既然java.util.concurrent中这么多并发工具选择扩展AbstractQueuedSynchronizer来实现锁、条件队列和条件谓词,那么,如何使用AbstractQueuedSynchronizer来实现我们自己的同步工具呢?

如果要扩展AbstractQueuedSynchronizer,强烈建议使用组合而非继承进行扩展,可以在自己的同步工具内部维持一个私有的AQS的子类,

这个AbstractQueuedSynchronizer的子类只需覆盖AbstractQueuedSynchronizer中的【当只支持独占操作时,即排它锁】

  1. tryAcquire(int);
  2. tryRelease(int);
  3. isHeldExclusively()方法【tryAcquire返回true时表示设置state成功,获取到锁了,返回false表示没获取到】;

或者覆盖AbstractQueuedSynchronizer中的【当支持共享获取时,即共享锁】,

  1. tryAcquireShared(int);
  2. tryReleaseShared(int)【tryAcquireShared返回值小于0时表示没获取到锁,等于0表示获取到锁但是后续线程获取不到锁,大于0表示获取到锁&后续线程可能也会获取到锁】;
    这些方法在AbstractQueuedSynchronizer中直接是throw new UnsupportedOperationException()。

在自己的工具类中使用子类继承AbstractQueuedSynchronizer时,直接实现这些方法,然后就可以使用这个子类的

  1. acquire(int);
  2. release(int);
  3. acquireShared(int);
  4. releaseShared(int);

来获取锁了。注意:AbstractQueuedSynchronizer中acquire(int)、release(int)、acquireShared(int)、releaseShared(int)都会调用子类中【因为被覆盖了】带有前缀try的版本来判断某个操作是否能执行(比如acquire(int)内部其实是会调用tryAcquire(int)来尝试获取锁,而tryAcquire(int)是AbstractQueuedSynchronizer本身就实现好的),所以我们只需要实现有前缀try的获取许可的方法版本。

ReentrantLock

ReentrantLock内部使用了AbstractQueuedSynchronizer来实现锁。其内部使用Sync扩展了AbstractQueuedSynchronizer。

如ReentrantLock中的Sync类:

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

    /**
     * Performs {@link Lock#lock}. The main reason for subclassing
     * is to allow fast path for nonfair version.
     */
    abstract void lock();

    /**
     * Performs non-fair tryLock.  tryAcquire is implemented in
     * subclasses, but both need nonfair try for trylock method.
     */
    final boolean nonfairTryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        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;
    }

    protected final boolean tryRelease(int releases) {
        int c = getState() - releases;
        if (Thread.currentThread() != getExclusiveOwnerThread())
            throw new IllegalMonitorStateException();
        boolean free = false;
        if (c == 0) {
            free = true;
            setExclusiveOwnerThread(null);
        }
        setState(c);
        return free;
    }

    protected final boolean isHeldExclusively() {
        // While we must in general read state before owner,
        // we don't need to do so to check if current thread is owner
        return getExclusiveOwnerThread() == Thread.currentThread();
    }

    final ConditionObject newCondition() {
        return new ConditionObject();
    }

    // Methods relayed from outer class

    final Thread getOwner() {
        return getState() == 0 ? null : getExclusiveOwnerThread();
    }

    final int getHoldCount() {
        return isHeldExclusively() ? getState() : 0;
    }

    final boolean isLocked() {
        return getState() != 0;
    }

    /**
     * Reconstitutes the instance from a stream (that is, deserializes it).
     */
    private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        s.defaultReadObject();
        setState(0); // reset to unlocked state
    }
}

其中,ReentrantLock内部的非公平锁和公平锁实现略有差别:

非公平锁为:

static final class NonfairSync extends Sync {
    private static final long serialVersionUID = 7316153563782823691L;

    /**
     * Performs lock.  Try immediate barge, backing up to normal
     * acquire on failure.
     */
    final void lock() {
        if (compareAndSetState(0, 1))
            setExclusiveOwnerThread(Thread.currentThread());
        else
            acquire(1);
    }

    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }
}

公平锁:

static final class FairSync extends Sync {
    private static final long serialVersionUID = -3000897897090466540L;


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


    /**
     * Fair version of tryAcquire.  Don't grant access unless
     * recursive call or no waiters or is first.
     */
    protected final boolean tryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        if (c == 0) {
            if (!hasQueuedPredecessors() &&
                compareAndSetState(0, acquires)) {
                setExclusiveOwnerThread(current);
                return true;
            }
        }
        else if (current == getExclusiveOwnerThread()) {
            int nextc = c + acquires;
            if (nextc < 0)
                throw new Error("Maximum lock count exceeded");
            setState(nextc);
            return true;
        }
        return false;
    }
}

然后调用NonFairSync或FairSync的lock()方法就可以获取到锁了。

由上可知,我们其实只需要实现AbstractQueuedSynchronizer中的try前缀方法就好了,然后我们就可以自由的通过acquire()或者release()等方法来获取锁和释放锁。

Semaphore

与ReentrantLock一样,Semaphore内部使用了AbstractQueuedSynchronizer来实现锁。其内部使用Sync扩展了AbstractQueuedSynchronizer。

Semaphore中的Sync类:

/**
 * Synchronization implementation for semaphore.  Uses AQS state
 * to represent permits. Subclassed into fair and nonfair
 * versions.
 */
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;
        }
    }

    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");
            if (compareAndSetState(current, next))
                return true;
        }
    }

    final void reducePermits(int reductions) {
        for (;;) {
            int current = getState();
            int next = current - reductions;
            if (next > current) // underflow
                throw new Error("Permit count underflow");
            if (compareAndSetState(current, next))
                return;
        }
    }

    final int drainPermits() {
        for (;;) {
            int current = getState();
            if (current == 0 || compareAndSetState(current, 0))
                return current;
        }
    }
}

其中,非公平信号量和公平信号量实现略有差别:

非公平信号量:

static final class NonfairSync extends Sync {
    private static final long serialVersionUID = -2694183684443567898L;


    NonfairSync(int permits) {
        super(permits);
    }


    protected int tryAcquireShared(int acquires) {
        return nonfairTryAcquireShared(acquires);
    }
}

公平信号量:

static final class FairSync extends Sync {
    private static final long serialVersionUID = 2014338818796000944L;

    FairSync(int permits) {
        super(permits);
    }

    protected int tryAcquireShared(int acquires) {
        for (;;) {
            if (hasQueuedPredecessors())
                return -1;
            int available = getState();
            int remaining = available - acquires;
            if (remaining < 0 ||
                compareAndSetState(available, remaining))
                return remaining;
        }
    }
}

然后调用NonFairSync或者FairSync的acquireShared(int)方法就可以获取到信号量了。

自定义简单的AbstractQueuedSynchronizer实现类:

首先,扩展AbstractQueuedSynchronizer类,

class MyLock {
    private Sync sync;

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

        @Override
        public boolean tryAcquire(int acqiure) {
            return compareAndSetState(0, 1);  //不是可重入的
        }

        @Override
        public boolean tryRelease(int acqiure) {
            setState(0);  // 不是可重入的
            return true;
        }
    }

    MyLock() { this.sync = new Sync(); }

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

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

其次,构建线程使用自己实现的同步工具,

class SyncThread implements Runnable {
    private MyLock lock;
    public SyncThread(MyLock lock) {
        this.lock = lock;
    }

    public void run() {
        lock.lock();
        for (int i = 0; i < 6; i ++) {
            System.out.print(ConcurrentTest.i++ + " ");
        }
        lock.unlock();
    }
}

最后,测试自己实现的同步工具,

public class ConcurrentTest {
    public static int i = 1;
    public static void main(String[] args) {
        MyLock lock = new MyLock();
        ExecutorService exec = new ThreadPoolExecutor(3, 1000, 60, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(2000), new ThreadPoolExecutor.CallerRunsPolicy());
        exec.execute(new SyncThread(lock));
        exec.execute(new SyncThread(lock));
        exec.execute(new SyncThread(lock));
    }
}

输出为,

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

总结

AbstractQueuedSynchronizer是Java中非常基础并且非常重要的抽象同步器。Java中诸多同步工具扩展了AbstractQueuedSynchronizer来实现锁、条件队列和条件谓词。我们也可以自己扩展AbstractQueuedSynchronizer来实现锁等。不过在工作中基本不会自己来实现AbstractQueuedSynchronizer,因为Java提供的ReentrantLock等等工具已经能够基本满足开发需求。

喜欢的可以关注微信公众号:
这里写图片描述

更多文章

  1. 我自己的头条号: 并发器AbstractQueuedSynchronizer
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值