`
森林的天空
  • 浏览: 15424 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

JUC 源码分析 三 AbstractQueuedSynchronizer 共享模式 与 CountDownLatch

 
阅读更多

共享模式

共享模式允许一组线程获取同一个许可。为实现共享模式子类需要实现两个方法:

  • tryAcquireShared:返回int类型的值,小于0表示获取失败,等于0表示获取成功但不允许后续更多的获取,大于0表示获取成功且允许更多的后续获取。
  • tryReleaseShared:返回true表示释放许可成功,可以唤醒等待线程;false表示失败,不唤醒等待线程。

共享获取 acquireShared

public final void acquireShared(int arg) {
    if (tryAcquireShared(arg) < 0)
        doAcquireShared(arg);
}

private void doAcquireShared(int arg) {
       // 添加到等待队列,不管是共享模式还是独占模式,都共享同一个等待队列。
    final Node node = addWaiter(Node.SHARED);
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
            final Node p = node.predecessor();
            if (p == head) {
                int r = tryAcquireShared(arg); // 尝试获取,返回值表示是否允许获取
                if (r >= 0) {
                   // 获取成功
                   // 把自己设为头结点并传递可以获取的信号
                   // node 把自己设为头结点后,它的后继发现它的前驱是头结点了,就会尝试获取。
                    setHeadAndPropagate(node, r);
                    p.next = null; // help GC
                    if (interrupted)
                        selfInterrupt();
                    failed = false;
                    return;
                }
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

private void setHeadAndPropagate(Node node, int propagate) {
    Node h = head; // Record old head for check below
    setHead(node);
    /*
     * 尝试通知队列里的下一个结点,如果:
     *       调用者指示或者之前操作记录显示需要传递
     *       (注意:这里对waitStatus使用单一检查,因为PROPAGATE可能被转换到SIGNAL)
     *   并且
     *       下一个结点以共享模式等待或者我们根本就不知道,因为它是空的。
     *
     * 在这些检查有点保守,可能导致不必要的唤醒,但只是在多重竞争acquires/releases时,
     * 因此,大多数都是现在或不久就需要通知的。
     */
    if (propagate > 0 || h == null || h.waitStatus < 0) {
        Node s = node.next;
        if (s == null || s.isShared())
            doReleaseShared();
    }
}

private void setHead(Node node) {
    head = node;
    node.thread = null; // for GC
    node.prev = null;
}

 

共享释放 releaseShared

释放共享许可的时候,最重要的是保证传递唤醒。

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

// 释放共享的核心方法
private void doReleaseShared() {
       // 要确保release传递,即使有其他正在进行的acquires/releases。
       // 这个过程的一般做法是尝试unpark head的后继,如果它(head)需要信号。
       // 如果head不需要信号,把状态设为PROPAGATE来确保一旦release,传递可以继续。
       // 另外,我们必须在循环里做这个,以免有新节点添加进来。
       // 不像unparkSuccessor的其他使用,我们需要知道CAS重置状态失败与否,如果失败,则重新检测。
    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
                }
                // CAS成功说明h结点需要通知后继,唤醒
                unparkSuccessor(h);
            }
            else if (ws == 0 &&
                     !compareAndSetWaitStatus(h, 0, Node.PROPAGATE)) {
                   // 如果是初始头结点,把其状态设为PROPAGATE,确保传递继续
                   // 状态更改失败,需要再次检测。
                continue;                // loop on failed CAS
            }
        }
        if (h == head) {  // loop if head changed
             // 如果操作过程中,头结点被改变(可能新增结点或者一个被唤醒线程把自己设为头结点了),需要再次检测。
            break;
        }
    }
}

CountDownLatch

作用:在完成一组正在其他线程中执行的操作之前,CountDownLatch允许一个或多个线程一直等待。CountDownLatch只阻塞一次,倒数到0之后,调用await方法的将直接通过。

看看实现阻塞与放行的内部类:

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

    Sync( int count) {
        setState(count); // 需要countDown的次数
    }

    int getCount() {
        return getState();
    }

    protected int tryAcquireShared( int acquires) {
       // 如果倒数到0,返回1表示允许后续获取,这样可以让AQS框架通知后继,
       // 否则返回-1表示失败,不能获取,线程会进入等待。
        return (getState() == 0) ? 1 : -1;
    }

    protected boolean tryReleaseShared( int releases) {
        // Decrement count; signal when transition to zero
        for (;;) { // CountDownLatch.countDown()可能被多线程调用,需要失败后重试
            int c = getState();
            if (c == 0)
                return false ;

            // 注意:这里是减1,而不是减去releases,因为CountDownLatch是对countDown调用次数的计数
            int nextc = c-1;
            if (compareAndSetState(c, nextc))
                return nextc == 0;
        }
    }
}

代码很简洁,因为AQS框架确实非常强大。

CountDownLatch类的一些方法:

public CountDownLatch( int count) {
    if (count < 0) throw new IllegalArgumentException( "count < 0");
    this.sync = new Sync(count);
}

public void await() throws InterruptedException {
    sync.acquireSharedInterruptibly(1);
}

public boolean await( long timeout, TimeUnit unit)
    throws InterruptedException {
    return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}

public void countDown() {
    sync.releaseShared(1);
}

本条目发布于2013 年 12 月 13 日。属于并发分类,被贴了 AQSCountDownLatc

分享到:
评论

相关推荐

    JUC并发编程与源码分析视频课.zip

    《JUC并发编程与源码分析视频课》是一门深入探讨Java并发编程的课程,主要聚焦于Java Util Concurrency(JUC)库的使用和源码解析。JUC是Java平台提供的一组高级并发工具包,它极大地简化了多线程编程,并提供了更...

    juc源码视频教程最全

    本教程将深入探讨JUC源码,旨在帮助你全面理解其背后的实现原理。 在Java中,JUC(java.util.concurrent)包包含了多种并发控制和同步组件,如线程池、锁、原子变量、并发容器等。这些组件设计精巧,性能优秀,能够...

    JUC AQS(AbstractQueuedSynchronizer)

    ReentrantLock Lock 加锁过程源码分析图,AQS 源码分析

    java8集合源码分析-JUC:高并发与多线程

    集合源码分析 高并发与多线程 Stargazers over time 线程 线程的创建和启动 线程的sleep、yield、join 线程的状态 代码在 部分。 synchronized关键字(悲观锁) synchronized(Object) 不能用String常量、Integer、Long...

    juc并发编程脑图以及相关示例代码

    juc并发编程脑图以及相关示例代码

    并发编程、juc工具包源码分析笔记

    Java 并发库(Java Util Concurrency, JUC)是 Java 平台中用于处理并发问题的核心工具包,提供了丰富的类和接口来简化并发编程。 在计算机系统中,进程是操作系统资源分配的基本单位,它拥有独立的内存空间,而...

    尚硅谷Java视频_JUC 视频教程

    尚硅谷_JUC线程高级_源码、课件 ·1. 尚硅谷_JUC线程高级_volatile 关键字与内存可见性 ·2. 尚硅谷_JUC线程高级_原子变量与 CAS 算法 ·3. 尚硅谷_JUC线程高级_模拟 CAS 算法 ·4. 尚硅谷_JUC线程高级_同步容器类...

    尚硅谷大厂必备技术之JUC并发编程基础视频 配套资料(自己根据视频整理课件,和代码)

    【尚硅谷】大厂必备技术之JUC并发编程视频 配套资料,自己根据视频整理 pdf 课件,和代码 视频地址:...

    根据尚硅谷JUC并发编程(对标阿里P6-P7)视频自己整理的pdf文档

    1、根据尚硅谷JUC并发编程(对标阿里P6-P7)视频自己整理的pdf文档 2、包含源码 视频地址:https://www.bilibili.com/video/BV1ar4y1x727/?p=1&vd_source=c634d163b940964d44747b4c3976117b 参考资料:...

    JUC+课程源码+线程操作

    在`juc_atguigu`这个压缩包中,包含了上述组件的示例代码,通过实际运行和分析这些代码,你可以深入理解JUC的工作原理和使用场景。同时,这些示例也能帮助你学习如何在实际项目中有效地利用JUC来提高并发程序的性能...

    尚硅谷JUC视频笔记整理,很详细和全面,帮你迅速掌握JUC

    JUC(Java Util Concurrent),即Java的并发工具包,是Java提供的一套并发编程解决方案,它通过一系列接口和类简化了并发编程的复杂性。本笔记整理涉及了JUC的内存可见性、volatile关键字以及CAS算法和原子变量等多...

    JUC线程锁框架

    在这个深度解析JUC线程锁框架的主题中,我们将探讨其核心组件、设计模式以及如何在实际应用中有效利用。 1. **原子变量(Atomic Variables)** JUC提供了一系列的原子变量类,如AtomicInteger、AtomicLong等,它们...

    这就是标题—— JUC.pdf

    JUC是什么 线程 进程 / 线程 线程状态 wait / sleep 并发 / 并行 Lock 使用Lock锁 可重入锁 公平锁 / 非公平锁 Synchronized / Lock 线程通讯 wait()、notify()和notifyAll() 虚假唤醒 Condition 定制化通信 多线程...

    MySQL、JVM、RocketMQ、JUC、设计模式、数据结构与算法学习总结.zip

    Java并发编程库(JUC)是Java标准库的一部分,提供了丰富的线程和同步工具,如Semaphore、CyclicBarrier、CountDownLatch和ExecutorService。理解并发原理,如线程安全、死锁和活锁,以及如何使用这些工具来实现高效...

    JUC(一)-AQS源码分析

    AQS源码分析一、锁的介绍1.1 乐观锁/悲观锁1.2 共享锁/独占锁1.3 公平锁/非公平锁1.4 小结二、AQS框架结构介绍2.1 类图2.2 AQS数据结构三、源码详解3.1 acquire源码详解3.2 release源码详解四、从ReentranLock看公平...

    尚硅谷JUC百度云连接

    根据提供的文件信息,“尚硅谷JUC百度云连接”这一标题和描述主要指向的是尚硅谷教育机构所提供的关于Java并发编程(Java Util Concurrency,简称JUC)的学习资源,并且通过一个百度网盘链接来分享这些资源。...

    个人学习JUC代码笔记总集

    Java并发编程领域中的JUC(Java Util Concurrency)是一门深奥且实用的技术,它包含在Java的`java.util.concurrent`包中,为多线程编程提供了高效、易用的工具。这个压缩包文件“个人学习JUC代码笔记总集”显然是一...

    艾编程coding老师:JUC 并发编程 + 底层原理.pdf

    JUC(java.util.concurrent)是Java提供的一个并发编程工具包...要掌握JUC并发编程及其底层原理,除了通过阅读官方文档和源码学习外,还需要大量实践和经验积累,才能够真正理解和应用JUC中的并发工具来解决实际问题。

    JUC AQS的加解锁.pdf

    它提供了实现排他模式(独占式)和共享模式锁的机制,被广泛应用于Java并发包中的ReentrantLock、Semaphore、CountDownLatch等组件中。 首先,AQS是一个抽象类,它的主要特点是使用一个int类型的变量(state)表示...

    尚硅谷Java视频_JUC视频教程

    为了解决这些问题,并进一步提高多线程程序的编写效率与可维护性,Java 5引入了JUC(Java Util Concurrency)包。JUC包提供了大量高级并发工具类,这些工具类简化了多线程编程的难度,使得开发者能够更加专注于业务...

Global site tag (gtag.js) - Google Analytics