Java多线程并发器之AbstractQueuedSynchronizer分析

来源:互联网 发布:波士顿矩阵图的优缺点 编辑:程序博客网 时间:2024/06/07 00:07

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
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 孩子吃了残奶怎么办 小米手机变卡了怎么办 主持时说错话了怎么办 小鲜肉老了不红怎么办 同学聚会大家玩手机你怎么办 率土之滨被掠夺怎么办 戒指戴手上取不下来怎么办 择离开我我该怎么办 解小手解不出来怎么办 学生把班里的班费弄丢了怎么办 班里选的班长成绩差怎么办 幼儿园班里孩子发生传染病怎么办 小仓鼠生了该怎么办 把老公生日忘了怎么办 老公说老婆脑子不好怎么办 和上司暧昧被同事发现怎么办 减肥不吃晚餐饿了怎么办 小孩晚饭吃多了怎么办 减肥晚上不吃饭饿了怎么办 两个人在一起性格不合怎么办 赌在你身上输了怎么办 苹果7lcould满了怎么办 e招贷不用了怎么办 牙活动了怎么办还疼 30岁掉了一颗牙怎么办? 在淘宝上交话费交错了怎么办 演出队在小区旁边扰民怎么办 雷雨天加了油怎么办 戴ok镜眼睛重影怎么办 乌龟背上长白色的花纹怎么办? 全自动洗衣机里面掉个硬币怎么办 跆拳道课上孩子乱动说话怎么办? 孩子不愿意上跆拳道课了怎么办 车座位里面倒了汤怎么办 腿被棍子打肿了怎么办 刚买的手机碎屏怎么办 被木棍么么打到头项怎么办 大王卡用到40g怎么办 王卡40g用完了怎么办 父亲把母亲打成重伤怎么办 狗狗脖子摔歪了怎么办