AbstractQueueSynchronizer

news2024/10/5 13:02:07

AbstractQueueSynchronizer

AbstractQueueSynchronizer 是基于 FIFO线程等待队列 的一个同步器开发框架。

这篇文章首先介绍同步器概念,然后介绍AQS的结构原理

什么是Synchronizer(同步器)

并发环境下,Synchronizer用于实现线程之间的协同。具体而言,就是哪个线程应该阻塞,哪个线程应该被唤醒。

常见的同步器实现有:CountdownLatchCycleBarrierReentrantLock等…

使用CountdownLatch进行协同的一个例子:

这里的一个例子是服务初始化,n个initializer并发执行,init方法内调用latch.await()等待所有initializer执行完成。

package org.otaku.example;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicInteger;

public class ServiceInit {
    private static final AtomicInteger ID_GENERATOR = new AtomicInteger(0);
    private final CountDownLatch latch;
    private final int count;

    public ServiceInit(int count) {
        latch = new CountDownLatch(count);
        this.count = count;
    }

    public void init() throws InterruptedException {
        for (int i = 0; i < count; i++) {
            new Initializer().start();
        }
        latch.await();
        System.out.println("all initializers finished!");
    }

    class Initializer extends Thread {
        private final int id = ID_GENERATOR.incrementAndGet();

        public void run() {
            try {
                Thread.sleep(ThreadLocalRandom.current().nextLong(1000, 3000));
            } catch (InterruptedException ignored) {
            }
            System.out.println("initializer-" + id + " finished!");
            latch.countDown();
        }

    }

    public static void main(String[] args) throws InterruptedException {
        ServiceInit serviceInit = new ServiceInit(5);
        serviceInit.init();
    }

}

AQS架构

AQS包含以下元素:

  • int类型的state字段存储状态
  • FIFO队列,队内元素是阻塞中的线程,这些线程在队列中等待被唤醒(当状态被更新)
  • 改变状态的方法,根据状态的改变,线程被阻塞入队,或者被唤醒出队

acquire

acquire方法用于申请互斥资源。

AbstractQueueSynchronizeracquire方法调用子类实现的tryAquire方法:

	//申请互斥资源
    public final void acquire(int arg) {
    	//调用tryAquire,如果返回true,则线程通过
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) //如果tryAquire返回false,则线程入队阻塞
            selfInterrupt();
    }

tryAquire留给子类实现:

//返回true,线程通过;返回false,线程入队阻塞
//调用acquire,tryAquire至少被调用一次,若返回false则入队阻塞,后续被唤醒后
//将继续尝试调用tryAquire,直到成功为止
protected boolean tryAcquire(int arg) {
        throw new UnsupportedOperationException();
    }

调用acquire流程如下:
acquire流程

release

release方法用于释放互斥资源
AbstractQueueSynchronizerrelease方法调用子类实现的tryRelease方法:

	//释放互斥资源
    public final boolean release(int arg) {
    	//调用tryRelease,返回true,唤醒队头线程;返回false则方法执行结束
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

tryRelease留给子类实现:

    protected boolean tryRelease(int arg) {
        throw new UnsupportedOperationException();
    }

一个简单的锁实现

Doug Lea在JDK文档中给出了使用AQS的一个例子:简单的不支持重入的互斥锁:

class Mutex implements Lock, java.io.Serializable {

   // Our internal helper class
   private static class Sync extends AbstractQueuedSynchronizer {
     // Acquires the lock if state is zero
     public boolean tryAcquire(int acquires) {
       assert acquires == 1; // Otherwise unused
       //CAS获取锁,从0到1则获取成功
       if (compareAndSetState(0, 1)) {
       	 //记录获取锁的线程
         setExclusiveOwnerThread(Thread.currentThread());
         //返回true,线程通过
         return true;
       }
       //获取锁失败,返回false,线程入队阻塞
       return false;
     }

     // Releases the lock by setting state to zero
     protected boolean tryRelease(int releases) {
       assert releases == 1; // Otherwise unused
       if (!isHeldExclusively())
         throw new IllegalMonitorStateException();
       setExclusiveOwnerThread(null);
       //将状态设置为0
       setState(0);
       //返回true,则唤醒队头阻塞线程,重新尝试tryAcquire
       return true;
     }

     // Reports whether in locked state
     public boolean isLocked() {
       return getState() != 0;
     }

     public boolean isHeldExclusively() {
       // a data race, but safe due to out-of-thin-air guarantees
       return getExclusiveOwnerThread() == Thread.currentThread();
     }

     // Provides a Condition
     public Condition newCondition() {
       return new ConditionObject();
     }

     // Deserializes properly
     private void readObject(ObjectInputStream s)
         throws IOException, ClassNotFoundException {
       s.defaultReadObject();
       setState(0); // reset to unlocked state
     }
   }

   // The sync object does all the hard work. We just forward to it.
   private final Sync sync = new Sync();

   public void lock()              { sync.acquire(1); }
   public boolean tryLock()        { return sync.tryAcquire(1); }
   public void unlock()            { sync.release(1); }
   public Condition newCondition() { return sync.newCondition(); }
   public boolean isLocked()       { return sync.isLocked(); }
   public boolean isHeldByCurrentThread() {
     return sync.isHeldExclusively();
   }
   public boolean hasQueuedThreads() {
     return sync.hasQueuedThreads();
   }
   public void lockInterruptibly() throws InterruptedException {
     sync.acquireInterruptibly(1);
   }
   public boolean tryLock(long timeout, TimeUnit unit)
       throws InterruptedException {
     return sync.tryAcquireNanos(1, unit.toNanos(timeout));
   }
 }

acquireShared

acquireShared方法用于申请共享资源

    public final void acquireShared(int arg) {
    	//1.调用tryAcquireShared,>= 0通过,< 0则进入线程等待队列
        if (tryAcquireShared(arg) < 0)
            doAcquireShared(arg);
    }

tryAcquireShared
tryAcquire不同,这里返回一个int,代表申请到的资源个数。返回<0代表资源申请失败;返回0代表申请资源成功,且后续申请都将失败;返回>0代表后续申请可能成功。

protected int tryAcquireShared(int arg) {
        throw new UnsupportedOperationException();
    }

releaseShared

releaseShared方法用于释放互斥资源

    public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
        	//唤醒阻塞线程
            doReleaseShared();
            return true;
        }
        return false;
    }

tryReleaseShared

    protected boolean tryReleaseShared(int arg) {
        throw new UnsupportedOperationException();
    }

CountdownLatch实现

使用CountdownLatch,多个线程在latch上等待,当latch被打开,多个线程都被放行,因此在latch上等待即为申请共享资源,而调用countdown方法则是尝试释放资源许可。

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

        Sync(int count) {
            setState(count);
        }

        int getCount() {
            return getState();
        }

        protected int tryAcquireShared(int acquires) {
        	//一旦latch被打开,线程将被放行,否则阻塞
            return (getState() == 0) ? 1 : -1;
        }

        protected boolean tryReleaseShared(int releases) {
            // Decrement count; signal when transition to zero
            for (;;) {
                int c = getState();
                //门已被打开,返回false,因为没有线程需要被唤醒
                if (c == 0)
                    return false;
                int nextc = c - 1;
                //CAS将状态-1
                if (compareAndSetState(c, nextc))
                	//只有状态减到0,才能释放等待线程,否则release都返回false
                    return nextc == 0;
            }
        }
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/115302.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Windows和Mac系统实现本地部署WebPageTest工具

在项目开发或者测试的过程中&#xff0c;由于没有上线&#xff0c;我们在公网上无法访问我们的网站&#xff0c;但同时我们又需要查看浏览器性能&#xff0c;这样我们就需要在本地部署WebPageTest工具以协助进行性能测试 具体实现步骤&#xff1a; Windows系统&#xff1a; …

FFT求多项式乘积

之前在b站上看到了一些介绍FFT的视频 《快速傅里叶变换(FFT)——有史以来最巧妙的算法&#xff1f;》 《这个算法改变了世界》 于是打算写一篇记录一下qwq&#xff08;本博客中的截图基本上来源于第一个视频&#xff09; Fast Fourier Transform 是一种能在O(nlogn)O(nlogn)…

企业营销数字化转型:如何转型、如何选品、如何用好?

省时查报告-专业、及时、全面的行研报告库省时查方案-专业、及时、全面的营销策划方案库【免费下载】2022年11月份热门报告盘点2023年&#xff0c;如何科学制定年度规划&#xff1f;《底层逻辑》高清配图清华大学256页PPT元宇宙研究报告.pdf&#xff08;附下载链接&#xff09;…

【LeetCode】1759. 统计同构子字符串的数目

统计同构子字符串的数目 题目描述 给你一个字符串 s &#xff0c;返回 s 中 同构子字符串 的数目。由于答案可能很大&#xff0c;只需返回对 109 7 取余 后的结果。 同构字符串 的定义为&#xff1a;如果一个字符串中的所有字符都相同&#xff0c;那么该字符串就是同构字符串…

自定义报表-FineReport JS实现隐藏Tab页

1. 概述 1.1 问题描述 在实际项目中&#xff0c;使用决策报表的时候&#xff0c;有时会用到在决策报表参数面板获取报表控件的值&#xff0c;那么该如何实现呢&#xff1f; 1.2 实现思路 使用 JS 获取报表主体的控件值&#xff1a; _g().getWidgetByName("area").…

【金猿人物展】数睿数据创始人兼CEO穆鸿:大数据价值创造关键在于应用普惠...

‍穆鸿本文由数睿数据创始人兼CEO穆鸿撰写并投递参与“数据猿年度金猿策划活动——2022大数据产业趋势人物榜单及奖项”评选。‍数据智能产业创新服务媒体——聚焦数智 改变商业事情还得从我2022年这一年经历的一些事情谈起&#xff0c;由于工作的原因&#xff0c;我要经常往返…

Python量化交易04——基于机器学习的交易策略

参考书目:深入浅出Python量化交易实战 学量化肯定要用的上机器学习这种强大的预测技术。本次使用机器学习构建一些简单的预测进行量化交易&#xff0c;使用Python进行回测。 获取数据 import pandas as pd import tushare as ts import numpy as npfrom sklearn.neighbors imp…

线程池设计与实现C

线程池实现 结构设计 先上图&#xff1a; 参数 线程池&#xff1a; 包含一个执行队列、一个任务队列mutex用来在多个线程取任务时锁任务队列&#xff0c;cond用来在任务队列为空时锁任务队列 如线程A锁了任务队列&#xff0c;去取任务时&#xff0c;又发现任务队列为空&…

【C++求解数学题】大圆圈里面三角形个数相等

本文介绍的问题是一道来自于二年级&#xff08;上&#xff09;数学的练习题。 问题 在下图中画8个Δ\DeltaΔ,使每个大圆圈里都有4个Δ\DeltaΔ. 示例&#xff1a; 每个大圆圈里面均有4个Δ\DeltaΔ. 方法 按照“变量-范围-条件”的三段式穷举法解题框架&#xff0c;对…

分布式系列之聊聊Nginx实现原理

Nginx作为开源的轻量级的HTTP服务器&#xff0c;广泛应用于分布式应用架构中。本文简要介绍了Nginx的特点及使用场景、Nginx的进程模型和请求处理流程&#xff0c;并结合不同场景进行配置&#xff0c;对Nginx的架构和实现原理有个初步的了解。 1、Nginx是什么 Nginx&#xff0…

Echarts之甘特图type: ‘custom‘参数详解

甘特图 const groupData XEUtils.groupBy(data, "eqpName"); //分组后的数据 const yAxisData Object.keys(groupData); const seriesData Object.keys(groupData).map((item, index) > {let arr [];groupData[item].forEach((GItem) > {arr.push([index,f…

Graphviz安装向导及入门指南

目录 1、首先在官网下载graphviz 2、安装。 3、测试并在Windows命令行中使用 4、在Python中使用 5、在自带的gvedit.exe 程序中使用 6、在语雀中使用 7、绘制一棵简单的二叉树 8、详细语法介绍 8.1 带标签 8.2 修改方框颜色和形状 8.3子视图 8.4 结构视图 8.5 …

【网络安全】Centos7安装杀毒软件----ClamAV

一、ClamAV介绍 Clam AntiVirus是一个Linux系统上使用的反病毒软件包。主要应用于邮件服务器&#xff0c;采用多线程后台操作&#xff0c;可以自动升级病毒库。 二、安装 1.下载rpm wget https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm 2.升级epe…

4.1、网络层概述

1、主要任务 网络层的主要任务是实现网络互连\color{red}实现网络互连实现网络互连&#xff0c;进而实现数据包在各网路之间的传输\color{red}实现数据包在各网路之间的传输实现数据包在各网路之间的传输 例如&#xff1a; 这些异构型网络若只是需要各自内部通信&#xff0c…

高质量发展指标构建:全国各省高质量发展需求(2014-2021年)

高质量发展是坚持更高层次和更高水平对外开放的发展。中国改革开放四十年的实践充分证明&#xff0c;不断扩大对外开放是推动中国经济社会发展的重要动力&#xff0c;是实现国家繁荣富强的根本出路。因此&#xff0c;在中国经济发展的新时代&#xff0c;推动新一轮高水平开放&a…

docker logs实时查看日志tail

docker logs实时查看日志tail docker logs -f -t --since="2017-05-31" --tail=10 container说明: --since : 指定输出日志开始日期。 -f : 查看实时日志 -t : 查看日志产生的时间戳 -tail=10 : 查看最后的10条日志。 container : 容器名docker logs -f --until=2s说…

Docker常用操作命令总结(一)

文章目录一、Docker的应用场景二、Docker 的优点三、Docker 架构四、安装Docker1、更新 apt 包索引2、安装docker3、安装完成之后&#xff0c;运行命令sudo docker info&#xff0c;检查安装状态4、有可能&#xff0c;第一次需要手动启动服务.就需要执行下面的命令&#xff0c;…

LabVIEW如何减少下一代测试系统中的硬件过时4

LabVIEW如何减少下一代测试系统中的硬件过时4 DSSP Class Definition DSSP父类定义有三种不同类型的函数:仅父类、公共类和基于度量的函数。DSSP父类&#xff0c;DSSP.Lvclass包含所有子类函数的超集&#xff0c;加上父类特有的一些函数。DSSP父类的单个子实例(例如AgSigGen.…

2022年总结(2022年1月1日至2022年12月25日)

前言 时光飞逝&#xff0c;又到了一年一度的年终总结的时间了&#xff0c;2022年充满磨难的一年&#xff0c;悲哉&#xff0c;痛哉~~ 但对于我而言&#xff0c;其实还好&#xff0c;基本无太大影响&#xff0c;黄金单身汉&#xff0c;一人吃饱&#xff0c;全家不饿~&#xff…

spring之手写框架

文章目录前言一、手写spring框架之核心接口实现二、手写spring框架之实例化Bean三、手写spring框架之获取所有set方法四、手写spring框架之给属性赋值4.1 非简单类型属性赋值4.2 简单类型属性赋值附&#xff1a;前言 Spring IoC容器的实现原理&#xff1a;工厂模式解析XML反射…