深入java并发包源码(二)AQS的介绍与使用

深入java并发包源码(一)简介

深入java并发包源码(二)AQS的介绍与使用

深入java并发包源码(三)AQS独占方法源码分析

AQS

本文章会讲解 AQS 的使用方法,然后通过 DEBUG 跟踪 AQS 执行的一系列操作来分析源码,读者跟着文章 DEBUG 跟踪源码能更容易理解。

AQS 是什么?

AbstractQueuedSynchronizer 队列同步器(AQS)是一个抽象类,作为并发工具的基础组件,为真正的实现类提供基础设施。并发工具是面向使用者的,AQS 面向的是并发工具的实现者。

AQS 的使用

AQS 有什么用?

AQS 提供了如构建同步队列,控制同步状态等方法,从设计模式角度来看,它采用了模板模式。它的主要使用方式是继承这个抽象类然后重写部分方法来实现自定义同步工具。

我们可以看到上面这些锁都是通过 AQS 实现的。

拿 ReentrantLock 来举例,它有一个内部类 Sync 继承了 AQS,并且重写了一些方法。然后将内部类的方法导出来给使用者使用。

讲的再多也不如自己动手实现一个并发工具理解的深刻,我们先介绍一下 AQS 的 API 然后用这些 API 来实现一个自定义的锁来理解它的使用方法。

AQS 分为可以重写的方法和不可以重写的方法,需要根据自己的需求去实现方法。

可以重写的方法:

方法名 功能
tryAcquire(int arg) 排它的获取这个状态。这个方法的实现需要查询当前状态是否允许获取,然后再进行获取(使用 compareAndSetState 来做)状态。
tryRelease(int arg) 释放状态
tryAcquireShared(int arg) 共享的模式下获取状态。
tryReleaseShared(int arg) 共享的模式下释放状态。
isHeldExclusively() 在排它模式下,状态是否被占用。

上面的方法只需要按照需求实现即可,比如 Reentrantlock 是独占的,就只需要实现 tryAcquiretryRelease 即可。当你实现 Semaphore 的时候就只需要实现 tryAcquireSharetryReleaseShared 方法即可。

不可重写的方法

这些方法被声明成 final 的,也就是不可重写的。后面对于源码的分析主要就是分析这些方法的源码。

方法名 功能
acquire(int arg) 独占式获取同步状态,如果当前线程获取同步状态成功,则由该方法返回
acquireInterruptibly(int arg) 可中断的获取同步状态,如果当前线程被中断,会抛出异常并返回
tryAcquireNanos(int arg) 可中断的获取同步状态,如果超时会返回 false
acquireShared(int arg) 共享的模式下获取同步状态
acquireSharedInterruptibly() 在排它模式下,状态是否被占用。
tryAcquireShareNanos() 共享式的 tryAcquireNanos()
release() 释放同步状态
releaseShared() 共享式释放同步
getQueueThreads() 获取等待在同步队列上的线程集合

现在我们需要实现一个独占锁,使用 AQS 提供的方法来重写 AQS 的模板方法:tryAcquiretryRelease

以下 demo 来自《Java 并发编程的艺术》

Sync .class

public class Sync extends AbstractQueuedSynchronizer {
    // 检测是否有线程持有锁只需看 state 是不是等于 1,state == 1 时
    // 锁被其他线程使用,当等于 0 时未被其他线程得到
    @Override
    protected boolean isHeldExclusively() {
        return getState() == 1;
    }

    // 尝试着获取锁,当 status 为 0 时获取成功返回 true,否则返回 false
     @Override
    protected boolean tryAcquire(int arg) {
        if (compareAndSetState(0, 1)) {
            setExclusiveOwnerThread(Thread.currentThread());
            return true;
        }
        return false;
    }

    // 尝试着释放锁
    @Override
    protected boolean tryRelease(int arg) {
        // 没有线程获取锁却释放
        if (getState() == 0) throw new IllegalArgumentException();
        setExclusiveOwnerThread(null);
        setState(0);
        return true;
    }
    public Condition newCondition() {
        return new ConditionObject();
    }

}

Mutex.class 用于导出 Sync 的方法

public class Mutex implements Lock {
    private 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.tryAcquire(1);
    }

    @Override
    public void unlock() {
        sync.release(0);
    }

    @Override
    public Condition newCondition() {
        return sync.newCondition();
    }

}

原文地址:https://www.cnblogs.com/stonymoon/p/9972089.html

时间: 2024-10-10 16:30:23

深入java并发包源码(二)AQS的介绍与使用的相关文章

深入java并发包源码(一)简介

深入java并发包源码(一)简介 深入java并发包源码(二)AQS的介绍与使用 深入java并发包源码(三)AQS独占方法源码分析 阅读本文章前需要了解 CAS 操作是什么. 首先大致介绍一下需要讲到的几个类,只需要理解这几个类是什么关系即可,后面会有详细解析. Unsafe :这个类提供了 native 方法,未开源,提供了线程阻塞和唤醒,原子操作等方法. LockSupport :包装了一层 Unsafe 类,非常类似于代理者模式,将在 Unsafe 类中的线程挂起唤醒等操作导出,避免将

Java并发包源码学习之AQS框架(一)概述

AQS其实就是java.util.concurrent.locks.AbstractQueuedSynchronizer这个类. 阅读Java的并发包源码你会发现这个类是整个java.util.concurrent的核心之一,也可以说是阅读整个并发包源码的一个突破口. 比如读ReentrantLock的源码你会发现其核心是它的一个内部类Sync: 整个包中很多类的结构都是如此,比如Semaphore,CountDownLatch都有一个内部类Sync,而所有的Sync都是继承自AbstractQ

Java并发包源码学习之AQS框架(二)CLH lock queue和自旋锁

上一篇文章提到AQS是基于CLH lock queue,那么什么是CLH lock queue,说复杂很复杂说简单也简单, 所谓大道至简: CLH lock queue其实就是一个FIFO的队列,队列中的每个结点(线程)只要等待其前继释放锁就可以了. AbstractQueuedSynchronizer是通过一个内部类Node来实现CLH lock queue的一个变种,但基本原理是类似的. 在介绍Node类之前,我们来介绍下Spin Lock,通常就是用CLH lock queue来实现自旋锁

Java并发包源码分析

并发是一种能并行运行多个程序或并行运行一个程序中多个部分的能力.如果程序中一个耗时的任务能以异步或并行的方式运行,那么整个程序的吞吐量和可交互性将大大改善.现代的PC都有多个CPU或一个CPU中有多个核,是否能合理运用多核的能力将成为一个大规模应用程序的关键. Java基础部分知识总结点击Java并发基础总结.Java多线程相关类的实现都在Java的并发包concurrent,concurrent包主要包含3部分内容,第一个是atomic包,里面主要是一些原子类,比如AtomicInteger.

Java并发包源码学习之线程池(一)ThreadPoolExecutor源码分析

Java中使用线程池技术一般都是使用Executors这个工厂类,它提供了非常简单方法来创建各种类型的线程池: public static ExecutorService newFixedThreadPool(int nThreads) public static ExecutorService newSingleThreadExecutor() public static ExecutorService newCachedThreadPool() public static Scheduled

如何在Eclipse中查看JDK以及Java框架的源码

对于Java程序员来说,有时候是需要查看JDK或者一些Java框架的源码来分析问题的,而默认情况下,你按住Ctrl,再点击 Java本身的类库(例如ArrayList)是无法查看源码的,那么如何在Eclipse中查看JDK以及Java框架的源码呢?下面,跟着我 一起,一步步带你走进源码的世界. 方法一:快速简单 第一步: 打开你的Eclipse,然后随便找一个Java文件,随便找一个Java类库,比如String什么的,然后按住Ctrl,再点击它,你会发现跳到如下界面: 你会发现报错了:Sour

【java集合框架源码剖析系列】java源码剖析之TreeMap

注:博主java集合框架源码剖析系列的源码全部基于JDK1.8.0版本.本博客将从源码角度带领大家学习关于ArrayList的知识. 一TreeMap的定义: public class TreeMap<K,V> extends AbstractMap<K,V> implements NavigableMap<K,V>, Cloneable, java.io.Serializable 可以看到TreeMap是继承自AbstractMap同时实现了NavigableMap,

【JDK1.8】 Java小白的源码学习系列:HashMap

目录 Java小白的源码学习系列:HashMap 官方文档解读 基本数据结构 基本源码解读 基本成员变量 构造器 巧妙的tableSizeFor put方法 巧妙的hash方法 JDK1.8的putVal方法 JDK1.8的resize方法 初始化部分 数组搬移部分 Java小白的源码学习系列:HashMap 春节拜年取消,在家花了好多天时间啃一啃HashMap的源码,同样是找了很多很多的资料,有JDK1.7的,也有JDK1.8的,当然本文基于JDK1.8.将所学到的东西进行整理,希望回过头再看

java企业站源码 响应式 兼容手机平板PC 主流SSM 框架 freemaker 静态引擎

java 企业网站源码 前后台都有 静态模版引擎, 代码生成器大大提高开发效率 前台: 支持三套模版, 可以在后台切换 点击:获取地址   QQ 313596790 官网 http://www.fhadmin.org/ 系统介绍: 1.网站后台采用主流的 SSM 框架 jsp JSTL,网站后台采用freemaker静态化模版引擎生成html 2.因为是生成的html,所以访问速度快,轻便,对服务器负担小 3.网站前端采用主流的响应式布局,同一页面同时支持PC.平板.手机(三合一)浏览器访问 4