首页 > 编程语言 >Semaphore源码简单解读

Semaphore源码简单解读

时间:2024-10-10 10:00:02浏览次数:8  
标签:AQS int 钩子 解读 tryAcquireShared 源码 Semaphore 方法

Semaphore源码解读

注意,阅读本文需要了解AQS,AQS采用了模板设计模式。后续本人会完善这篇文章

Semaphore的方法

  1. acquire()
    阻塞获得一个许可,会阻塞,直到得到一个可用许可或被中断
    重载版本 acquire(n) :尝试获取n个许可
  2. acquireUninterruptibly()
    类acquire,但不可中断
  3. tryAcquire()
    非阻塞版本,只尝试一次,可被中断
    重载版本tryAcquire(n),tryAcquire(timeout,TimeUnit) 限时尝试,没抢到许可会等待直到得到许可或被中断 。tryAcquire(n,timeout,TimeUnit)
  4. release()
    释放一个许可。重载:release(n)
  5. drainPermits()
    当前线程剩余的许可
  6. hasQueuedThreads()
    判断是否队列中有等待许可的线程
  7. getQueuedLength()
    等待队列的长度

Semaphore总体框架

和ReentrantLock内部类似,Semaphore内部定义了一个抽象同步器Sync抽象类,它继承了AbstractQueuedSynchronizer。Sync有两个子类:NofairSync和FairSync,分别实现了非公平和公平两种版本的实现。Semaphore的各种acquire方法都是委托给Sync对象调用的。
Sema类

关于permit

Sync的构造方法。permit的本质是AQS的state

Sync(int permits) {
		// AQS的setState方法,
            setState(permits);
        }

Semaphore的 acquire方法

// Semaphore中
public void acquire() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }

AQS的模板方法acquireSharedInterruptibly,调用了tr·yAcquireShared钩子方法

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

FairSync

FairSync重写了AQS的tryAcquireShared方法

protected int tryAcquireShared(int acquires) {
            for (;;) {
			//  若有前驱,则申请失败。保证申请者为第一个节点
                if (hasQueuedPredecessors())
                    return -1;
			//  获取当前剩余许可量
                int available = getState();
                int remaining = available - acquires;
			// 若剩余许可<0,或cas成功,返回剩余量。
			// 为什么剩余量<0也要照样返回呢?
                if (remaining < 0 ||
                    compareAndSetState(available, remaining))
                    return remaining;
            }
        }

再看AQS的方法

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

第二个if块有两种情况:

  1. 钩子方法tryAcquireShared返回负数
    即remaining<0,申请量大于许可量,则进入方法doAcquireSharedInterruptibly(arg)。由于钩子方法中使用短路或,remaining<0则不会cas交换
  2. remaining>0,cas成功,得到许可,则直接跳出方法,线程则继续下面的代码。在tryAcquireShared钩子方法中,进行过一次cas,若cas成功,返回remain,remain必然>=0,如果cas失败,则重新自旋,重复流程。

AQS的doAcquireSharedInterruptibly方法

private void doAcquireSharedInterruptibly(int arg)
        throws InterruptedException {
		//进入等待队列
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
		// 死循环,
            for (;;) {
				//获得前驱节点
                final Node p = node.predecessor();
                // 若前驱为head,则进入申请方法
				// 也就是说,非队首节点不能进入该申请方法
				if (p == head) {
				// 调用钩子方法
                    int r = tryAcquireShared(arg);
                    //r>0则成功得到许可,跳出方法
					if (r >= 0) {
					//	该方法中,将当前节点设为head,并改变状态
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        failed = false;
                        return;
                    }
                }
				//进行挂起判断
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
private void setHeadAndPropagate(Node node, int propagate) {
        Node h = head; // Record old head for check below
        setHead(node);
        if (propagate > 0 || h == null || h.waitStatus < 0 ||
            (h = head) == null || h.waitStatus < 0) {
            Node s = node.next;
            if (s == null || s.isShared())
                doReleaseShared();
        }
    }

总结申请方法

Semaphore的 acquire调用了AQS的模板方法acquireSharedInterruptibly,acquireSharedInterruptibly进一步调用了钩子方法tryAcquireShared进行申请许可,在钩子方法中的死循环中,钩子方法退出循环的方式有两种:(1)remaining<0;(2)remaining<=0且cas成功。若计算出剩余许可remaining<0,则直接返回remaining,若remaining>=0,则进行CAS,若成功,返回r,该r也必然>=0。
在模板方法acquireSharedInterruptibly中,只有所需许可>可用许可时,才会进入方法doAcquireSharedInterruptibly,该方法封装线程节点入队,并且只有队首节点(前驱为head)才能够进行tryAcquireShared钩子调用,申请成功则它成为head,并且unpark后驱。

Acquire时的入队条件

  1. 模板的if中,调用tryAcquireShared钩子方法,返回负数(申请量大于可用量)。在FairSync中,队列中有其他节点时,会直接返回-1以进入队列
  2. 只要在钩子方法中,remaining>0,则会重复循环,直到成功或者r<0进入队列。
  3. NofairSync与FairSync相比,只是钩子方法去掉了队列中是否有结点的判断

标签:AQS,int,钩子,解读,tryAcquireShared,源码,Semaphore,方法
From: https://www.cnblogs.com/nammonco/p/18455735

相关文章

  • Android14 如何更改无源码应用图标
    没有源码的Android应用一般就是在解析该APK时就要替换图标,如果只在Launcher替换,那么Settings中很多地方都要进行适配,修改比较麻烦,现在提供一种在源头就替换的涉及修改的文件frameworks/base/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java......
  • 游戏百科网站系统,这个项目要怎么去设计呢?附源码
    其实设计一个游戏百科网站系统,如同打造一把钥匙,开启通往无限游戏世界的门户。这个系统将是一座桥梁,连接着热爱游戏的玩家们与无尽游戏知识。它不仅需要是信息丰富的百科全书,还应是活泼、互动的社区。现在,让我们开始这段设计之旅。需求分析与目标设定玩家的声音:我们深入论坛,......
  • 就业岗位数据分析,这个项目要怎么去设计呢?附源码
        其实设计一个就业岗位数据分析程序如同在一座繁华的城市中建造一座指引灯塔,旨在帮助求职者在复杂的就业市场中找到他们的方向。这个系统不仅需要照亮当前的路,还应当预见未来的潮流,为政策制定者提供决策支持。现在,让我们像讲述一个故事一样,娓娓道来这个设计过程。......
  • 多模态大语言模型(MLLM)-InstructBlip深度解读
    前言InstructBlip可以理解为Blip2的升级版,重点加强了图文对话的能力。模型结构和Blip2没差别,主要在数据集收集、数据集配比、指令微调等方面下文章。创新点数据集收集:将26个公开数据集转换为指令微调格式,并将它们归类到11个任务类别中。使用了其中13个数据集来进行指令......
  • 第2天:熟悉Android Studio补充材料——`MainActivity.kt`解读
    下面是对“第2天:熟悉AndroidStudio”该文学习的更深层次的补充材料,对MainActivity.kt文件的理解。下面对MainActivity.kt文件中每一行进行详细解释:packagecom.example.helloworldappimportandroid.os.Bundleimportandroidx.activity.enableEdgeToEdgeimporta......
  • Java项目实战II基于Java+Spring Boot+MySQL的墙绘产品展示交易平台设计与实现(源码+数
    目录一、前言二、技术介绍三、系统实现四、文档参考五、核心代码六、源码获取全栈码农以及毕业设计实战开发,CSDN平台Java领域新星创作者,专注于大学生项目实战开发、讲解和毕业答疑辅导。获取源码联系方式请查看文末一、前言在当今多元化、个性化的家居装饰市场中,墙......
  • Java项目实战II基于Java+Spring Boot+MySQL的作业管理系统设计与实现(源码+数据库+文
    目录一、前言二、技术介绍三、系统实现四、文档参考五、核心代码六、源码获取全栈码农以及毕业设计实战开发,CSDN平台Java领域新星创作者,专注于大学生项目实战开发、讲解和毕业答疑辅导。获取源码联系方式请查看文末一、前言在教育信息化的大背景下,作业管理作为教学......
  • 黑马毕设分享《基于springboot招生管理系统》(源码+lw+部署文档+讲解等)
    文章目录1.前言黑马设计——专注大学生的项目实战开发,免费讲解,毕业答疑辅导黑马设计工作室简介:黑马设计是一家专注大学生的项目实战开发,免费讲解,毕业答疑辅导的工作室✅,创始人是硕士毕业于华南理工大学,工科专业,目前团队成员全职+兼职上百余人,运营线上店铺2家,与B站(IT实战,黑......
  • 黑马毕设分享《基于springboot学生综合测评系统》(源码+lw+部署文档+讲解等)
     文章目录1.前言黑马设计——专注大学生的项目实战开发,免费讲解,毕业答疑辅导黑马设计工作室简介:黑马设计是一家专注大学生的项目实战开发,免费讲解,毕业答疑辅导的工作室✅,创始人是硕士毕业于华南理工大学,工科专业,目前团队成员全职+兼职上百余人,运营线上店铺2家,与B站(IT实战......
  • 一文详细解读自动驾驶与机器人所需各种传感器的原理与优缺点
    更多优质内容,请关注公众号:智驾机器人技术前线1.激光雷达(LiDAR)工作原理:激光雷达通过发射短脉冲的激光束,测量光束从目标物体反射回来所需的时间(即飞行时间),从而计算出物体的距离。LiDAR通常通过旋转激光发射器来获取360度的视场,生成点云数据,反映周围环境的三维信息。优势:高......