Java多线程编程-AbstractQueuedSynchronizer(AQS)同步队列

AbstractQueuedSynchronizer

我们在进行ReentrantLock和ReentrantReadWriteLock使用的时候,我们查看源码的时候,是使用Sync抽象类进行处理,Sync是继承了AbstractQueuedSynchronizer进行相应的操作,源码如下:

//此锁的同步控制基础,在下面细分为公平和非公平版本
//使用AQS(AbstractQueuedSynchronizer)状态表示锁的保留数
abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = -5179523762034025860L;

        /**
         * 执行Lock lock,子类化的主要原因是允许为非公平版本提供快速路径
         */
        abstract void lock();

        /**
         * 执行不公平的tryLock,tryAcquire是在子类中实现的,但是都需要对trylock方法进行不公平的尝试
         */
        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() {
            //虽然我们必须在拥有者之前先以一般状态读取状态
            //但我们不需要这样做就可以检查当前线程是否为拥有者
            return getExclusiveOwnerThread() == Thread.currentThread();
        }

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

        // 从外部类继承的方法

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

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

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

        /**
         * 从流重构实例(即反序列化它)
         */
        private void readObject(java.io.ObjectInputStream s)
            throws java.io.IOException, ClassNotFoundException {
            s.defaultReadObject();
            setState(0); // 重置为解锁状态
        }
    }

看到Sync中的方法,是实现了AbstractQueuedSynchronizer 相关方法,我们看看AbstractQueuedSynchronizer 的类图:
在这里插入图片描述
这个图片有点大,可以自行在idea中查看即可。
AbstractQueuedSynchronizer提供一个框架,用于实现依赖于先进先出(FIFO)等待队列的阻塞锁和相关的同步器(信号灯,事件等)。此类旨在为大多数依赖单个原子int值表示状态的同步器提供有用的基础。子类必须定义更改此状态的受保护方法,并定义该状态对要获取或释放的对象而言意味着什么。有了这些,该类中的其他方法将执行所有排队和阻塞机制。子类可以维护其他状态字段,但仅跟踪通过同步方式使用方法getState,setState和compareAndSetState操作的原子更新的int值。
子类应定义为用于实现其封闭类的同步属性的非公共内部帮助器类。类AbstractQueuedSynchronizer没有实现任何同步接口。相反,它定义了诸如 acquisitionInterruptible之类的方法,这些方法可以由具体的锁和相关的同步器适当地调用以实现其公共方法。
此类支持默认的exclusive模式和shared模式之一或两者。当以独占模式进行获取时,其他线程尝试进行的获取将无法成功。由多个线程获取的共享模式可能(但不一定)成功。此类不会“理解”这些差异,只是从机械意义上说,当成功获取共享模式时,下一个等待线程(如果存在)还必须确定它是否也可以获取。在不同模式下等待的线程共享相同的FIFO队列。通常,实现子类仅支持这些模式之一,但例如可以在ReadWriteLock中发挥作用。仅支持独占模式或仅支持共享模式的子类无需定义支持未使用模式的方法。

有类图可以看出,AbstractQueuedSynchronizer类定义了一个嵌套的ConditionObject类,该类可以由支持独占模式的子类用作Condition实现,为此方法isHeldExclusively报告是否相对于当前线程,方法专门保持同步。使用当前getState值调用的release完全释放该对象,并且给定已保存的状态值,acquisition最终将该对象恢复为其先前的获取状态。否则,没有AbstractQueuedSynchronizer方法会创建这样的条件,因此,如果无法满足此约束,请不要使用它。ConditionObject的行为当然取决于其同步器实现的语义。
AbstractQueuedSynchronizer类提供内部队列的检查,检测和监视方法,以及条件对象的类似方法。可以根据需要使用AbstractQueuedSynchronizer将它们导出到类中以实现其同步机制。此类的串行化仅存储基本的原子整数维护状态,因此反序列化的对象具有空线程队列。需要可序列化的典型子类将定义一个readObject方法,该方法可在反序列化时将其恢复为已知的初始状态
要将AbstractQueuedSynchronizer类用作同步器的基础,请通过使用getState,setState和或compareAndSetState检查和/或修改同步状态来重新定义相关方法。
默认情况下,这些方法中的每一个都会引发UnsupportedOperationException。这些方法的实现必须在内部是线程安全的,并且通常应简短且不阻塞。定义这些方法是only支持的使用此类的方法。所有其他方法都声明为final,因为它们不能独立变化。你可能还会发现从AbstractOwnableSynchronizer继承的方法对于跟踪拥有专有同步器的线程很有用。鼓励使用它们-这将启用监视和诊断工具,以帮助用户确定哪些线程持有锁。
由于AbstractQueuedSynchronizer是一个FIFO的双向队列,内部是通过节点head和tail记录队首和对尾,队列中保存的元素类型为Node,我们可以看看Node类:

AbstractQueuedSynchronizer.Node

//等待队列
/**
*等待队列是“ CLH”(Craig,Landin和Hagersten)锁定队列的变体。 CLH锁通常用于自旋锁。
相反,我们将它们用于阻塞同步器,但使用相同的基本策略,将有关线程的某些控制信息保存在其节点的前身中。
每个节点中的“状态”字段跟踪线程是否应阻塞。节点的前任释放时会发出信号。否则,队列的每个节点都充当一
个特定通知样式的监视器,其中包含一个等待线程。虽然状态字段不控制是否授予线程锁等。线程可能会尝试获取
它是否在队列中的第一位。但是先行并不能保证成功。它只赋予了抗辩的权利。因此,当前发布的竞争者线程可能
需要重新等待。
     +------+  prev +-----+       +-----+
     * head |      | <---- |     | <---- |     |  tail
     *      +------+       +-----+       +-----+
*/
static final class Node {
    /** 指示节点正在共享模式下等待的标记 */
    static final Node SHARED = new Node();
    /** 指示节点正在以独占模式等待的标记 */
    static final Node EXCLUSIVE = null;
    /** waitStatus值,指示线程已取消 */
    static final int CANCELLED =  1;
    /** waitStatus值,指示后续线程需要取消停放 */
    static final int SIGNAL    = -1;
    /** waitStatus值,指示线程正在等待条件 */
    static final int CONDITION = -2;
    /**
     * waitStatus值,指示下一个acquireShared应该无条件传播
     */
    static final int PROPAGATE = -3;
    volatile int waitStatus;
    volatile Node prev;
    volatile Node next;
    volatile Thread thread;
    Node nextWaiter;

    /**
     * 如果节点在共享模式下等待,则返回true
     */
    final boolean isShared() {
        return nextWaiter == SHARED;
    }

    /**
     * Returns previous node, or throws NullPointerException if null.
     * Use when predecessor cannot be null.  The null check could
     * be elided, but is present to help the VM.
     *
     * @return the predecessor of this node
     */
    final Node predecessor() throws NullPointerException {
        Node p = prev;
        if (p == null)
            throw new NullPointerException();
        else
            return p;
    }

    Node() {    // 用于建立初始标头或SHARED标记
    }

    Node(Thread thread, Node mode) {     // Used by addWaiter
        this.nextWaiter = mode;
        this.thread = thread;
    }

    Node(Thread thread, int waitStatus) { // Used by Condition
        this.waitStatus = waitStatus;
        this.thread = thread;
    }
}

Node中的thread变量用来存放进入AQS队列里面的线程,Node节点内部的SHARED用来标记该线程是获取共享资源时被阻塞挂起后放入AQS队列的,EXCLUSIVE 用来标记线程是获取独占资源时被挂起后放入的,waitStatus记录当前线程等待状态。prev记录当前节点的上一个节点,next记录当前节点的下一个节点。
AQS中维护了一个单一的状态信息state,可以通过getState,setState和CompareAndSetState函数修改其值。在ReentrantLock和ReentrantReadWriteLock和Semaphore信号量中都有用到。在不同的类中代表的含义不一样。
通过源码可以看出,AQS中有个内部类ConditionObject,用来结合锁实现线程的同步。

通过上面的原理,我们自定义同步器

基于AQS实现不可重入的独占锁。
CustomeNoReentrantLock:

public class CustomeNoReentrantLock implements Lock, Serializable {

    private static final long serialVersionUID = 6142703832416865725L;

    private static class Sync extends AbstractQueuedSynchronizer {

        private static final long serialVersionUID = -5633158763632936629L;

        /***
         * 是否锁已经被持有
         * @return
         */
        @Override
        protected boolean isHeldExclusively() {
            return getState() == 1;
        }

        /***
         * 如果state为0 则尝试获取锁
         * @param arg
         * @return
         */
        @Override
        protected boolean tryAcquire(int arg) {
            if (compareAndSetState(0, 1)) {
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }

        /***
         * 释放锁 设置state为0
         * @param arg
         * @return
         */
        @Override
        protected boolean tryRelease(int arg) {
            if (getState() == 0) {
                throw new IllegalMonitorStateException();
            }
            setExclusiveOwnerThread(null);
            setState(0);
            return true;
        }

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

    //创建一个sync对象做相应的操作
    private final Sync sync = new Sync();

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

    @Override
    public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }

    @Override
    public boolean tryLock() {
        return sync.tryAcquire(1);
    }

    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return sync.tryAcquireNanos(1, unit.toNanos(time));
    }

    /***
     * 解锁
     */
    @Override
    public void unlock() {
        sync.release(1);
    }

    /***
     * 创建一个condition对象
     * @return
     */
    @Override
    public Condition newCondition() {
        return sync.newCondition();
    }
}

CustomeNoReentrantLockMain:

public class CustomeNoReentrantLockMain {
    final static CustomeNoReentrantLock customeNoReentrantLock = new CustomeNoReentrantLock();
    final static Condition condition = customeNoReentrantLock.newCondition();
    final static Condition condition2 = customeNoReentrantLock.newCondition();
    final static Queue<String> q = new LinkedBlockingQueue<>();
    final static int qSize = 20;

    public static void main(String[] args) {
        List<Thread> threadProducerList = new ArrayList<>();
        List<Thread> threadConsumerList = new ArrayList<>();
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                //获取独占锁
                customeNoReentrantLock.lock();
                System.out.println("producer current thread name:" + Thread.currentThread().getName() + " get lock");
                try {
                    //队列如果满了就不往队列中添加元素,并等待释放锁
                    while (qSize == q.size()) {
                        System.out.println("current thread name:" + Thread.currentThread().getName() + " call wait");
                        condition2.await();
                    }
                    //向队列中添加数据
                    q.add("add a element to q");
                    System.out.println("add element end");
                    //唤醒消费线程
                    condition.signalAll();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    //释放锁
                    customeNoReentrantLock.unlock();
                }
            }
        };

        Runnable runnable1 = new Runnable() {
            @Override
            public void run() {
                //获取独占锁
                customeNoReentrantLock.lock();
                System.out.println("customer current thread name:" + Thread.currentThread().getName() + " get lock");
                try {
                    //队列为空则等待往队列中添加
                    while (0 == q.size()) {
                        System.out.println("current thread name:" + Thread.currentThread().getName() + " call wait");
                        condition.await();
                    }
                    //消费
                    String pop = q.poll();
                    System.out.println("poll element end");
                    //唤醒生产线程
                    condition2.signalAll();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    //释放锁
                    customeNoReentrantLock.unlock();
                }
            }
        };

        for (int i = 0; i < 20; i++) {
            threadProducerList.add(new Thread(runnable, "thread0" + i));
            threadConsumerList.add(new Thread(runnable1, "thread0" + i));
        }
        for (int i = 0; i < 20; i++) {
            threadProducerList.get(i).start();
            threadConsumerList.get(i).start();
        }
    }
}

运行结果:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值