美文网首页
AQS-独占与共享

AQS-独占与共享

作者: 囧囧有神2号 | 来源:发表于2018-05-18 00:48 被阅读0次

AQS的功能可以分为两类:独占与共享;如ReentrantLock利用了其独占功能,CountDownLatch,Semaphore利用了其共享功能。
AQS的静态内部类Node里有两个变量,独占锁与共享锁在创建自己的节点时(addWaiter方法)用于表明身份,它们会被赋值给Node的nextWaiter变量。

        static final Node SHARED = new Node();
        static final Node EXCLUSIVE = null;

独占锁

独占锁就是每次只允许一个线程执行,当前线程执行完会release将同步状态归零,再唤醒后继节点,这里通过自定义tryAcquire来实现公平与非公平(即是否允许插队);
acquire & release

//成功代表同步状态的变更,排斥其他线程;否则加入等待队列
    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
//归零同步状态,唤醒后继节点
    public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

这里通过同步状态来实现独占功能。

共享锁

如Semaphore,CountDownLatch,它们调用的是AQS里的acquireSharedInterruptibly与releaseShared;实现自己的tryAcquireShared与tryReleaseShared,这里便体现了独占与共享的不同,独占锁的tryAcquire,tryRelease返回boolean代表同步状态更改的成功与否;tryAcquireShared返回int值,tryAcquireShared返回0代表当前线程能够执行,但之后的将会进入等待队列中;返回正数直接执行,之后的线程可能也可以直接执行;
以Semphore的非公平锁为例,如果当前正在执行的线程数小于限制值,就CAS更改同步状态值,线程直接执行。返回负数代表正在执行的线程数达到允许值。

        final int nonfairTryAcquireShared(int acquires) {
            for (;;) {
                int available = getState();
                int remaining = available - acquires;
                if (remaining < 0 ||
                    compareAndSetState(available, remaining))
                    return remaining;
            }
        }

Semphore限制同时执行的线程数,当一个线程acquire成功
acquireShared

    public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        if (tryAcquireShared(arg) < 0)
            doAcquireSharedInterruptibly(arg);
    }

    private void doAcquireSharedInterruptibly(int arg)
        throws InterruptedException {
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
            for (;;) {
                final Node p = node.predecessor();
                if (p == head) {
                    int r = tryAcquireShared(arg);
                    if (r >= 0) {
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        failed = false;
                        return;
                    }
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
//这里对中断的处理也不同,共享式的线程被唤醒后发现中断标记后,会直接抛中断异常
//而独占锁采用的中断策略是恢复中断标记,也就是交给调用方去处理中断
                   throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

releaseShared
tryReleaseShared由子类实现,返回boolean,以Semphore为例

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

当一个线程执行完会增加我们的同步状态值,返回true,之后doReleaseShared唤醒head.next节点里的线程。

    public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }

    private void doReleaseShared() {
        for (;;) {
            Node h = head;
            if (h != null && h != tail) {
                int ws = h.waitStatus;
                if (ws == Node.SIGNAL) {
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                        continue;            // loop to recheck cases
                    unparkSuccessor(h);
                }
                else if (ws == 0 &&
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    continue;                // loop on failed CAS
            }
            if (h == head)                   // loop if head changed
                break;
        }
    }

    private void unparkSuccessor(Node node) {
        int ws = node.waitStatus;
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);

        Node s = node.next;
        if (s == null || s.waitStatus > 0) {
            s = null;
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
        if (s != null)
            LockSupport.unpark(s.thread);
    }

doReleaseShared在之前介绍Semaphore里说过;若有后继节点就唤醒它;若没有(即ws == 0,因为waitStatus的值是后继节点赋予的)则将head节点waitStatus改为PROPAGATE,之后进入的节点会将此head的状态改为SIGNAL,具体实现在shouldParkAfterFailedAcquire里。

总结

独占与共享最大不同就在各自的tryacquire里,对于独占来说只有true或false,只有一个线程得以执行任务;而对于共享锁的tryAcquireShared来说,线程数没达到限制都可以直接执行。
但本质上都是对AQS同步状态的修改,一个是0与1之间,另一个允许更多而已。

相关文章

  • AQS-独占与共享

    AQS的功能可以分为两类:独占与共享;如ReentrantLock利用了其独占功能,CountDownLatch,...

  • JUC-(11)AQS(中)-共享模式

    AQS(上)-独占模式 AQS(中)-共享模式 AQS(下)-Condition 对比 独占模式共享模式acqui...

  • AQS之独占和共享锁

    AQS独占和共享锁,ReentantLock为独占锁,ReentantReadWriteLock中readLock...

  • Semaphore加锁与释放流程

    背景 与ReentrantLock的独占(Exclusive)不同的是 , Semaphore是共享类型的(Sha...

  • 07 番外 基于AQS实现一个锁

    1 独占锁 2 共享锁

  • Mysql索引介绍

    这节讲述的是InnoDB使用的锁类型: 包含: Shared and Exclusive Locks(共享锁与独占...

  • Java Semaphore/CountDownLatch/Cy

    前言 前面分析了基于AQS的独占锁ReentrantLock、共享锁/独占锁ReentrantReadWriteL...

  • 深入理解AQS(二)- 共享模式

    共享锁与独占锁 独占锁被某个线程持有时,其他线程只能等待当前线程释放后才能去竞争锁,而且只有一个线程能竞争锁成功。...

  • Java虚拟机内存tips

    java虚拟机内存可以分为独占区和共享区。 独占区:虚拟内存栈、本地方法栈、程序计数器。 共享区:方法区、Java...

  • 是共享还是独占?

    提起共享这个词我脑中首先想到的是两个和电脑有关的名词:一个是共享文件,就是网络不发达时期局域网内互传文件用的那个东...

网友评论

      本文标题:AQS-独占与共享

      本文链接:https://www.haomeiwen.com/subject/yqcidftx.html