`
Jen
  • 浏览: 57453 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

JUC代码浅析[1]——同步器AQS

    博客分类:
  • java
阅读更多

 

JUC代码浅析[1]——同步器AQS

       AQSAbstractQueuedSynchronizer)是一个提供实现各种锁和同步器的基本框架,它实现了调度逻辑,留出具体的进入和释放规则给子类实现。JUC中基于AQS实现的有ReentrantLock,Semaphore,CountDownLatch, ReetrantRead WriteLock,FutureTask等。代码非常复杂很难描述清楚每个细节,可能还有一些理解偏差的地方。总的来说底层实现的要点有Unsafe.compareAndSwapXXX(Object o,long offset,int expected,int x);Unsafe.park() 挂起线程 ;Unsafe.unpark()唤醒被park的线程;双向列表存储线程的队列。

       park的线程有3种方式可以唤醒它,park返回时不会报告是被哪种方式唤醒的,可以用Thread.interrupted()检查,

l  unpark(Thread thread),这种方式唤醒时Thread.interrupted()返回的是false

l  thread.interrupt(),这种方式唤醒时Thread.interrupted()返回的是true

l  不知原因的返回

 

       队列中存放着Node代表等待线程,它包含4中状态CANCELLED(1):当前节点已经退出了,SIGNAL(-1):后继者需要被唤醒,CONDITION(-2):节点在condition队列中等待,0;非负数代表节点不需要被唤醒了。节点有共享和互斥两种模式。

AQS提供给子类一个int state属性通过getState()setState()暴露给子类,子类通过state和实现以下5个方法来实现不同的需求

l  tryAcquire  互斥模式下尝试获得许可(一般需要结合state的值

l  tryRelease 互斥模式下尝试释放许可并设置state

l  tryAcquireShared  共享模式下尝试获得许可

l  tryReleaseShared 共享模式下尝试释放许可并设置state

l  isHeldExclusively 当前线程是否占有许可

       外部获取许可总的来说就是尝试获取许可,不成功时进入列表队列等待再次尝试或挂起或被打断。具体实现看互斥不可中断模式的acquire方法,

public final void acquire(int arg) {

        if (!tryAcquire(arg) &&

            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))

            selfInterrupt();

    }

       首先尝试获取许可(也就是子类要实现的),不成功时当前线程添加到列表的最后并等待,必要时挂起或打断线程(注意何时挂起何时打断)

其中addWaiter方法主要使用自旋锁和Unsafe.compareAndSwapXXX方法保证列表状态的一致.

       acquireQueued方法不断的检查和移动队列,当当前线程到达位于队列头下一个的时候尝试获取

    final boolean acquireQueued(final Node node, int arg) {

        try {

            boolean interrupted = false;

            for (;;) {

                final Node p = node.predecessor();

                if (p == head && tryAcquire(arg)) {

                    setHead(node);

                    p.next = null; // help GC

                    return interrupted;

                }

                if (shouldParkAfterFailedAcquire(p, node) &&

                    parkAndCheckInterrupt())

                    interrupted = true;

            }

        } catch (RuntimeException ex) {

            cancelAcquire(node);

            throw ex;

        }

    }

 

下面看如何检查和移动队列的

    private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {

        int s = pred.waitStatus;

        if (s < 0)

            /*

             * 前一个节点的等待状态小于0,则当前节点被park起来

             *

             */

            return true;

        if (s > 0) {

            /*

             * 前面节点是退出状态,则调整指针往前查询跳过其他退出状态的节点,

             * 直到找到不是退出状态的节点作为它的前一个节点

             */

        do {

       node.prev = pred = pred.prev;

        } while (pred.waitStatus > 0);

        pred.next = node;

    }

        else

            /*

             * 标识当前节点需要被signal(以前一个节点状态为SIGNAL表示),以免期间发生        * 了请求操作需要继续自旋检查

             */

            compareAndSetWaitStatus(pred, 0, Node.SIGNAL);

        return false;

    }

 

释放许可。互斥不可中断模式的release方法,

    public final boolean release(int arg) {

        if (tryRelease(arg)) {

            Node h = head;

            if (h != null && h.waitStatus != 0)

                unparkSuccessor(h);

            return true;

        }

        return false;

    }

尝试释放成功时,唤醒列表队列的下一个节点(如果需要的话)

    private void unparkSuccessor(Node node) {

        /*

         * 清除节点的SIGNAL状态 (接下来的唤醒操作之后,表示它的下一个节点不需要被唤        * 醒了)

         */

        compareAndSetWaitStatus(node, Node.SIGNAL, 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);

    }

 

       另外还有互斥可中断模式、共享不可中断模式、共享可中断模式。

       可中断和不可中断模式的区别在于可中断模式接收到中断信号后立即中断,不可中断模式要等待节点到达列表第一个位置才中断(中断过程还要进行一些操作)

       共享模式尝试获取是执行tryAcquireShared方法并把节点标识为共享模式,其他的操作跟互斥模式有所不同但大同小异。具体的就不详细展开了。

 

      

 

分享到:
评论

相关推荐

    狂神说JUC代码狂神说JUC代码

    在Java中,JUC(java.util.concurrent)包包含了多种并发控制和同步工具,如线程池、锁、并发容器等,这些工具使得开发者能够编写高效、安全、可维护的并发代码。以下将详细讲解其中的一些关键知识点: 1. **线程池...

    juc aqs java

    juc 的aqs介绍。

    Java 多线程与并发(10-26)-JUC锁- 锁核心类AQS详解.pdf

    其中,AbstractQueuedSynchronizer(简称AQS)是构建各种同步器的核心组件。 AQS是一个抽象的队列同步器,它基于FIFO(先进先出)队列来管理线程的排队和获取共享资源。它是实现Java并发包中锁和其他同步器的基础...

    个人学习JUC代码笔记总集

    这个压缩包文件“个人学习JUC代码笔记总集”显然是一个个人的学习资源,记录了对JUC组件的理解和应用实例,特别适合已经有一定Java基础,想要深入学习并发编程的开发者。 JUC的主要目标是简化并发编程,提高多线程...

    JUC代码收集,java高并发多线程学习

    1. **concurrent**:这是JUC库的主要部分,提供了线程池、同步容器、并发集合等类。例如: - `ExecutorService`和`ThreadPoolExecutor`:它们构成了线程池的基础,允许开发者有效地管理和调度线程,避免过度创建...

    AQS和JUC知识点讲解

    4. **自定义同步器**:AQS提供了一套模板方法,允许开发者定义如何获取和释放资源,从而创建自定义的同步组件,如ReentrantLock和Semaphore等。 二、Java Util Concurrency(JUC) JUC是Java并发编程的工具包,...

    JUC核心类AQS的底层原理

    AQS作为Java并发工具包(JUC)中的一个核心抽象类,其设计目的是为了实现各种同步器(如锁、信号量等)。AQS主要通过三个核心组成部分来实现这些同步组件的功能: 1. **State变量及其CAS操作**:AQS维护了一个名为`...

    java并发编程:juc、aqs

    `AQS`(AbstractQueuedSynchronizer)是JUC库中的一个关键组件,它是一个抽象基类,为构建自定义的同步器提供了基础框架。AQS通过内部维护一个基于链表的等待队列,有效地管理线程的同步和唤醒,从而实现锁和其他同步...

    JUC AQS的加解锁.pdf

    AQS是一种框架,用来构建锁或其他同步组件的基础。它提供了实现排他模式(独占式)和共享模式锁的机制,被广泛应用于Java并发包中的ReentrantLock、Semaphore、CountDownLatch等组件中。 首先,AQS是一个抽象类,它...

    JUC(一)-AQS源码分析

    为了学习JUC,AQS是基础中的基础,所以我们首先深入了解下AQS。 一、锁的介绍 为了了解AQS的源码,我们需要先大概下锁中的一些功能 1.1 乐观锁/悲观锁 乐观锁与悲观锁是一种广义上的概念,体现了看待线程同步的不同...

    这就是标题—— JUC.pdf

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

    Java——JUC

    JUC主要包含在`java.util.concurrent`包中,提供了线程池、同步容器、并发集合、原子类、异步计算以及定时任务等多种高级并发工具。 1. **线程池(ExecutorService)** - 线程池是JUC中的核心组件,它允许开发者...

    juc学习代码。。。。

    JUC库极大地简化了多线程编程,提供了丰富的同步机制、并发数据结构以及线程池等高级特性。在这个"juc学习代码"的资源中,我们很显然会接触到Java并发编程的核心概念和实践。 首先,JUC库中的`java.util.concurrent...

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

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

    JUC代码演示 Java多线程并发

    示例中的代码展示了如何使用`synchronized`关键字来实现售票系统的同步: ```java public class test2 { static class Ticket { private Integer sum; public Ticket(Integer sum) { this.sum = sum; } ...

    JUC AQS(AbstractQueuedSynchronizer)

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

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

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

    JUC1 指令翻译 v1.0.rar

    "JUC1指令翻译v1.0.rar"这个压缩包文件包含的资源旨在帮助我们了解如何将微命令和指令翻译成十六进制和二进制代码,并生成微程序控制器的入口地址。 首先,我们要了解微指令和指令的区别。指令是高级语言中的语句,...

    JUC个人笔记1

    在Java并发编程中,"JUC个人笔记1"主要探讨了多线程并发控制的一些核心概念和技巧。首先,遵循高内聚低耦合的原则,将线程的操作与资源类进行解耦,使得代码结构更加清晰,易于维护。在多线程交互中,特别是涉及到`...

    juc入门案例演示代码

    在这个"juc入门案例演示代码"中,我们将会探讨两个关键的JUC组件:`JUCLock`和`process_demo1`。 首先,`JUCLock`很可能是指Java中的锁机制,它是JUC库中的核心部分。在Java中,传统的`synchronized`关键字提供了一...

Global site tag (gtag.js) - Google Analytics