当前位置: 首页 > news >正文

AQS 源码解读

一、AQS

AQSAbstractQueuedSynchronizer 的简称,又称为同步阻塞队列,是 Java 中的一个抽象类。在其内部维护了一个由双向链表实现的 FIFO 线程等待队列,同时又提供和维护了一个共享资源 state ,像我们平常使用的 ReentrantLock、Semaphore、ReentrantReadWriteLock、SynchronousQueue、FutureTask等都是基于AQS 进行实现的。

AQS 可以实现什么功能呢?在 AQS 中不考虑资源的获取和释放,主要关注资源获取不到时,如何将线程加入队列以及阻塞,当释放资源后,如何再进行线程的出列和唤醒。而对于资源的操作则交予具体实现的子类进行完成。

在此基础上 AQS 为了使线程的控制可以更灵活,又提供了两种同步模型,独占模式共享模式

  • 独占模式:表示并发情况下只有一个线程能执行,其余则需等待,例如 Lock 锁,一次只能有一个线程获取到锁。

  • 共享模式:允许多线程根据规则执行,例如 Semaphore 进行多个线程的协调。

AQS 已经帮我们实现了队列的维护,以及线程的等待和唤醒,但是具体资源的获取和释放都需要由继承类实现,对于资源的获取和释放也是区分了独占模式和共享模式,相应方法如下:

//查询是否正在独占资源,condition会使用
boolean isHeldExclusively()	
//独占模式,尝试获取资源,成功则返回true,失败则返回false
boolean tryAcquire(int arg)
//独占模式,尝试释放资源,成功则返回true,失败则返回false
boolean tryRelease(int arg)
//共享模式,尝试获取资源,如果返回负数表示失败,否则表示成功。
int tryAcquireShared(int arg)
//共享模式,尝试释放资源,成功则返回true,失败则返回false。
boolean tryReleaseShared(int arg)

例如在 ReentrantLock 公平锁中,tryAcquire 的实现逻辑如下:

protected final boolean tryAcquire(int acquires) {// 当前线程final Thread current = Thread.currentThread();// AQS 中共享 stateint c = getState();if (c == 0) {// 如果队列中没有其他线程,并对state进行修改,// 如果修改成功则设置独占锁的线程为当前线程if (!hasQueuedPredecessors() &&compareAndSetState(0, acquires)) {setExclusiveOwnerThread(current);return true;}}else if (current == getExclusiveOwnerThread()) {// 如果独占线程就是当前线程,则是重入的场景,对 state + 1int nextc = c + acquires;if (nextc < 0)throw new Error("Maximum lock count exceeded");setState(nextc);return true;}// 如果都没成功,则获取锁失败return false;}
}

可以看到在 ReentrantLock 公平锁中,通过 state 的值来标识是否有锁资源可用,并且重入情况下也是对 state 的值进行修改标识 ,对于 state 的修改和判断是否有等待队列线程, AQS 中都提供了相应的方法。

AQS 中几个核心的方法如下,同样区分了独占模式和共享模式:

// 返回共享资源的当前值
final int getState()
// 设置共享资源的值
final void setState(int newState)
// CAS设置共享资源的值
final boolean compareAndSetState(int expect, int update)// 独占模式获取同步资源,会调用重写的tryAcquire(int arg),
// 如果获取成功,则不做任何处理,否则将会加入同步队列并挂起线程等待
final void acquire(int arg)
// 独占模式式获取同步资源,但是可以响应中断
final void acquireInterruptibly(int arg)
// 独占模式获取同步资源,但多出了超时时间,
// 如果当前线程在 nanosTimeout 时间内没有获取到同步资源,
// 那么将会返回false,否则返回true
final boolean tryAcquireNanos(int arg, long nanosTimeout)
// 独占模式式释放同步资源,会调用重写的 tryRelease(int arg) 方法,
// 在释放同步资源之后,会将同步队列中第一个节点包含的线程唤醒
final boolean release(int arg)// 共享模式式获取同步资源,会调用重写的 tryAcquireShared(int arg) ,
// 如果当前线程未获取到同步资源,会加入同步队列等待,
// 和独占式的区别这里 tryAcquireShared(int arg) < 0 时才认为未获取到资源
final void acquireShared(int arg)
// 共享模式式获取同步资源,可以响应中断
final void acquireSharedInterruptibly(int arg)
// 共享模式获取同步资源,但多出了超时时间
final boolean tryAcquireSharedNanos(int arg, long nanosTimeout)
// 共享式释放同步资源,会调用重写的 tryReleaseShared(int arg) 方法,
// 在释放同步资源之后,会将同步队列中第一个节点包含的线程唤醒
final boolean releaseShared(int arg)

下面一起从源码的角度,分析 AQS 是如何实现线程的协调和管理的。

二、共享资源 state

共享资源 state 就是 AQS 中的一个 int 类型的全局变量,使用了 volatile 进行修饰,保证了多线程下的数据可见性,并且 AQS 为其提供了普通和 CAS 方式的修改方法,该共享资源主要用来做资源的标记。

例如:

ReentrantLock 锁中用来表示是否获取到锁,默认情况 0 表示无锁状态,获取到锁后进行 +1 ,如果是重入的场景下同样进行 +1 ,最后释放锁后再进行 -1

Semaphore 中用来表示信号量的标记,当获取信号量时 state 进行 -1 ,释放信号量再进行 +1

在这里插入图片描述
在这里插入图片描述

三、FIFO 阻塞队列

AQS 中阻塞队列采用双向链表进行实现,具体源码如下:

	//等待队列节点类,双向链表static final class Node {// 标记,指示节点正在共享模式下等待static final Node SHARED = new Node();// 标记,指示节点正在独占模式下等待static final Node EXCLUSIVE = null;//  waitStatus值表示线程已取消static final int CANCELLED = 1;//  waitStatus值表示后继线程需要唤醒static final int SIGNAL = -1;//  waitStatus值,表示线程正在等待状态static final int CONDITION = -2;// waitStatus值指示下一个被获取的应该无条件的传播static final int PROPAGATE = -3;// 线程等待状态volatile int waitStatus;// 上一个节点volatile Node prev;// 下一个节点volatile Node next;// 当前线程volatile Thread thread;// 节点的模式,独占还是贡献Node nextWaiter;// 是否为共享模型final boolean isShared() {return nextWaiter == SHARED;}// 获取上一个节点final Node predecessor() throws NullPointerException {Node p = prev;if (p == null)throw new NullPointerException();elsereturn p;}Node() {    // Used to establish initial head or SHARED marker}Node(Thread thread, Node mode) {     // Used by addWaiterthis.nextWaiter = mode;this.thread = thread;}Node(Thread thread, int waitStatus) { // Used by Conditionthis.waitStatus = waitStatus;this.thread = thread;}}

其中通过 nextWaiter 表示当前线程是独占模型还是共享模式,线程的所属状态使用 waitStatus 来进行表示,其中包括:

  • 默认值为 0,表示当前节点在 sync 队列中,等待着获取资源。
  • CANCELLED,值为1,表示当前的线程被取消。
  • SIGNAL,值为-1,释放资源后需唤醒后继节点。
  • CONDITION,值为-2, 等待condition唤醒。
  • PROPAGATE,值为-3,工作于共享锁状态,需要向后传播,比如根据资源是否剩余,唤醒后继节点。

四、独占模式 acquire 获取资源

AQS acquire() 方法中,首先调用子类的 tryAcquire 获取资源,如果资源获取成功则不做任何处理,如果失败则首先使用 addWaiter 将当前线程加入到队列中,并指定 Node 的类型为独占模式:

在这里插入图片描述

addWaiter 方法中,会将当前线程的 Node 加入到队列的尾端,如果尾节点为空或修改尾节点失败则进入到 enq 中使用自旋的方式修改:

在这里插入图片描述

enq 方法中可以看出,当为节点为空时,也就是队列中无数据时,会初始化一个空的 Head 节点。

在这里插入图片描述

再回到 acquire() 方法,加入队列后会进入到 acquireQueued 方法中,在该方法循环中如果当前节点 的pred 上一个节点是 head 节点的话,那该节点不就是第一个节点吗,因为从上面就可以看出,初始情况下 head 是一个空的 node ,那 head 的下一个节点不就是第一个进入到队列的节点了,这种情况下遵循队列先进先出的原则,再次尝试是否能获取到资源,如果可以成功获取资源到则将当前节点置为 head 节点,同时再次将 head 节点置为空 node,此时线程也无需阻塞可以直接执行:

在这里插入图片描述
在这里插入图片描述

但是如果当前节点的上一个节点不是 head 节点,或者没有获取到资源,则此时需要进行挂起阻塞,下面首先会触发 shouldParkAfterFailedAcquire 方法,这里先看后面的 parkAndCheckInterrupt 方法,该方法主要做了将线程挂起阻塞的作用,采用 LockSupport.park 进行线程的阻塞:

在这里插入图片描述

在这里插入图片描述

再来看 shouldParkAfterFailedAcquire 方法就是控制当前线程是否需要挂起,这里就需要使用到 Node 中的 waitStatus,在该方法中有三种类型的判断:

  • 如果当前是 SIGNAL 状态则可以直接挂起
  • waitStatus大于 0 时,在 NodewaitStatus 大于 0 的状态就是 CANCELLED 状态,也就是标识线程被取消了,此时这种线程进行阻塞也就没有意义了,那就一直循环向上取线程未被取消的作为当前节点,继续执行。
  • waitStatus小于等于 0 时,将状态置为 SIGNAL 类型

在这里插入图片描述

后面当阻塞的线程被唤醒后,会继续在 acquireQueued 的循环中,不断找寻第一个入队的线程进行尝试获取资源操作。

五、独占模式 release 释放资源

release 方法中,首先会调用子类的 tryRelease 方法释放资源:

在这里插入图片描述
然后会将当前的 head 节点传入 unparkSuccessor 方法中,在该方法中首先将该Node节点的 waitStatus 修改到默认的 0 值,然后获取到下一个节点,因为 head 节点始终保持为空节点,下一个节点才是真正的队列中第一个线程。但如果下一个节点为空的话,或者已经被取消了,则循环从 tail 节点向上找最前面正常的节点,最后直接使用 LockSupport.unpark 唤醒该节点的线程:

在这里插入图片描述

六、共享模式 acquireShared 获取资源

acquireShared 方法中,会首先调用子类的 tryAcquireShared 方法获取资源,但与独占模式不同的是,这里当资源的数量小于 0 时,则认为获取资源失败:

在这里插入图片描述
当资源获取失败时,会进入到 doAcquireShared 方法,在该方法中同样先将自己加入到阻塞队列中,将 Node 的类型设为 Node.SHARED 共享模式:

在这里插入图片描述

下面的判断逻辑和独占模式差不多,取当前节点的上一个节点,如果是 head 节点,那当前节点便是队列的第一个线程,此时则可以尝试获取资源,如果资源大于 0 认为获取资源成功,则将当前节点置为 head 节点:

在这里插入图片描述

setHeadAndPropagate 方法中,与独占模式不同,将当前节点置为 head 节点后并没有进行置空操作,而且又会判断资源大于 0 的话,通过 doReleaseShared 唤醒更多的线程继续执行:

这里 doReleaseShared 方法的逻辑,在下面 releaseShared解读时进行解释:

在这里插入图片描述

回到 doAcquireShared 方法中,下面 shouldParkAfterFailedAcquireparkAndCheckInterrupt 则和独占模式调用方法相同,将符合条件的线程进行阻塞:

在这里插入图片描述
后面当阻塞的线程被唤醒后,会继续在 doAcquireShared 的循环中,不断找寻第一个入队的线程进行尝试获取资源操作。

七、共享模式 releaseShared 释放资源

releaseShared 方法中,会首先调用子类的 tryReleaseShared 方法释放资源:

在这里插入图片描述

释放资源后会进到 doReleaseShared 方法唤醒等待的线程,对 head 节点进行唤醒:

在这里插入图片描述

head 节点唤醒后,会和 doAcquireShared 的方法中的 setHeadAndPropagate 形成呼应,如果获取到的资源数大于 0 则继续使用 doReleaseShared 进行唤醒,从而控制多个线程执行。

八、总结

AQS 没有限制具体某个场景的应用,但通过其内部维护的 FIFO 队列和共享资源 state便可以实现很多种不同的场景,在阅读了 AQS 源码后,应该有了更深入的理解,后面再去看 ReentrantLock、Semaphore 等的源码会发现很容易理解。

相关文章:

AQS 源码解读

一、AQS AQS 是 AbstractQueuedSynchronizer 的简称&#xff0c;又称为同步阻塞队列&#xff0c;是 Java 中的一个抽象类。在其内部维护了一个由双向链表实现的 FIFO 线程等待队列&#xff0c;同时又提供和维护了一个共享资源 state &#xff0c;像我们平常使用的 ReentrantLo…...

使用 DataLoader 加载数据报错‘expected sequence of length 4 at dim 1 (got 0)’

使用 transformer 将字符串转为 id 序列&#xff0c;字符串为中英文混杂形式&#xff0c; 运行中出现报错&#xff1a;expected sequence of length 4 at dim 1 (got 0) 发现是在encoder_plus转换时&#xff0c;将输入的文本根据max_length截断了&#xff0c;导致[MASK]等字段…...

第十四届蓝桥杯第三期模拟赛B组C/C++原题与详解

文章目录 一、填空题 1、1 找最小全字母十六进制数 1、1、1 题目描述 1、1、2 题解关键思路与解答 1、2 给列命名 1、2、1 题目描述 1、2、2 题解关键思路与解答 1、3 日期相等 1、3、1 题目描述 1、3、2 题解关键思路与解答 1、4 乘积方案数 1、4、1 题目描述 1、4、2 题解关…...

致敬三八女神节,致敬IT女生

前言 三八女神节是一个特别的节日&#xff0c;它是为了纪念所有的女性&#xff0c;表达对她们的尊重和关爱。在这个特别的节日里&#xff0c;我们想要致敬所有在IT领域中奋斗的女生&#xff0c;她们用自己的智慧和努力为这个世界带来了无限的可能。 IT女神 从事IT行业的女生…...

【Go语言学习笔记】数据

目录字符串数组数组初始化指针复制切片基本操作resliceappendcopy字典deletemap是引用类型并发操作字符串 字符串是不可变字节&#xff08;byte&#xff09;序列&#xff0c;其本身是一个复合结构 type stringStruct struct{str unsafe.Pointerlen int }头部指针指向字节数组…...

puzzle(0919)六宫数局

目录 六宫数局 示例题目 简单模式 普通模式 困难模式 六宫数局 最强大脑同款项目。 找出一条给定起点和终点的路径&#xff0c;每一步的方向任选&#xff0c;在这个方向上移动的步数是当前数的质因数分解中2、3、5的次数。 示例题目 按照六边形坐标系来建立坐标系&#…...

脑机接口科普0016——独立BCI与非独立BCI

本文禁止转载&#xff01;&#xff01;&#xff01;&#xff01; 所谓的“独立BCI”与“非独立BCI”仅仅是BCI系统中的一个术语。本章主要是介绍一下这两个术语。 这两个术语是由Wolpaw在2002年提出来的。 独立BCI是指不依赖于中枢神经系统的的输出。 非独立BCI是指那种依赖…...

女神节告白代码

今天是女神节&#xff0c;送给所有女神们一句话&#xff1a; 爱自己是终生浪漫的开始&#xff0c;无论何时都要好好爱自己 目录 1. 请求动画帧填充 2.点类 3.粒子类 ​编辑 4.ParticlePool 池类 5.创建和填充 6.处理循环队列 7.更新活动粒子 8.移除非活性粒子 9.绘制有…...

【数据结构】单链表:头部操作我很行,插入也不用增容!!!

单链表 文章目录单链表1.链表1.1链表的概念和结构1.2链表的分类2.单链表的模拟实现2.1单链表的打印2.2单链表的尾插2.3单链表的头插2.4单链表的尾删2.5单链表的头删2.6单链表的查找2.7单链表的中间插入(在结点前插入)2.8单链表的中间删除(删除该结点)2.9单链表的中间插入(在结点…...

SpringBoot——使用WebSocket功能

springboot自带websocket&#xff0c;通过几个简单的注解就可以实现websocket的功能&#xff1b; 启动类跟普通的springboot一样&#xff1a; /*** 2023年3月2日下午4:16:57*/ package testspringboot.test7websocket;import org.springframework.boot.SpringApplication; im…...

博弈论小课堂:非零和博弈(实现双赢)【纳什均衡点】

文章目录 引言I 非零和博弈1.1 囚徒问题1.2 博弈中双方的收益矩阵II 在现实中找均衡点2.1 博弈通常不是一次性的,而是反复进行的2.2 博弈论讲的都是阳谋的策略2.3 人类还处于文明的初级阶段,人的道德水准不容高估2.4 乌合之众效应2.5 很多时候看似是双赢,其实是在更大范围内…...

数组中的逆序对

解题思路1&#xff1a; 看到这个题目&#xff0c;我们的第一反应是顺序扫描整个数组。每扫描到一个数组的时候&#xff0c;逐个比较该数字和它后面的数字的大小。如果后面的数字比它小&#xff0c;则这两个数字就组成了一个逆序对。假设数组中含有n个数字。由于每个数字都要和…...

C++基础了解-01-基础语法

基础语法 一、基础语法 C 程序可以定义为对象的集合&#xff0c;这些对象通过调用彼此的方法进行交互。现在让我们简要地看一下什么是类、对象&#xff0c;方法、即时变量。 对象 - 对象具有状态和行为。例如&#xff1a;一只狗的状态 - 颜色、名称、品种&#xff0c;行为 -…...

phpmyadmin 文件包含(CVE-2014-8959)

0x01 漏洞介绍 phpMyAdmin是phpMyAdmin团队开发的一套免费的、基于Web的MySQL数据库管理工具。该工具能够创建和删除数据库,创建、删除、修改数据库表,执行SQL脚本命令等。phpMyAdmin的GIS编辑器中libraries/gis/GIS_Factory.class.php脚本存在目录遍历漏洞。远程攻击者可借助…...

SpringBoot集成MyBatis

目录 实现步骤 1. 在项目的 pom.xml 配置文件中引入如下依赖 2. 在项目的 application.properties 配置文件中添加如下依赖 3. 新建 UserMapper.class 接口类&#xff0c;添加如下 3 个方法 4. 在 /resources/mybatis/mapper 路径(需要手动创建文件夹)下创建 UserMapper.xm…...

MySQL-索引

索引介绍索引是对数据库表中一列或者多列的值进行排序的一种结构&#xff0c;使用索引可提高数据库中特定数据的查询速度。索引是一个单独的、存储在磁盘上的数据库结构&#xff0c;它们包含着对数据表里所有记录的引用指针。使用索引用于快速找出在某个或多个列中有一特定值得…...

【STM32存储器映射-寄存器基地址-偏移】

前言 在学习STM32的时候&#xff0c;我们看到很多的寄存器编程&#xff0c; 比方说LED灯&#xff1a; //GPIOB.5端口输出高电平GPIOB->ODR|1<<5; //PB.5 输出高GPIOE->ODR|1<<5; //PE.5输出高 //GPIOB端口全部输出高电平*(unsigned int*)(0x4001 …...

【华为OD机试2023】最多颜色的车辆 C++ Java Python

【华为OD机试2023】最多颜色的车辆 C++ Java Python 前言 如果您在准备华为的面试,期间有想了解的可以私信我,我会尽可能帮您解答,也可以给您一些建议! 本文解法非最优解(即非性能最优),不能保证通过率。 Tips1:机试为ACM 模式 你的代码需要处理输入输出,input/cin接收…...

特斯拉后端面试(部分)

HR告知如果面试通过要转.net-_- round1 有没有用过Java新版本&#xff0c;知道有哪些特性吗&#xff1f;A&#xff1a;没有。Q&#xff1a;我们基本在用JDK11&#xff0c;有的新项目用到JDK17了。参考答案1&#xff1a; ZGC: A Scalable Low-Latency Garbage Collector Epsi…...

【python】使用python将360个文件夹里的照片,全部复制到指定的文件夹中,并且按照顺序重新命名

最近要做一个图像生成的课题&#xff0c;在网上找了一个混合的数据集。这个数据集中一共有360个文件夹&#xff0c;然后文件夹中有6-9张不等的照片&#xff0c;我的目标就是编写python代码将所有的照片取出来&#xff0c;放到一个指定的文件夹里&#xff0c;并且从1开始按照顺序…...

【C语言】3天速刷C语言(初识)

【声明】本篇博客只用于对与刚学习C语言的同学的一个初始了解&#xff0c;具体内容请继续关注本专栏后续内容。什么是C语言C语言是一门通用计算机编程语言&#xff0c;广泛应用于底层开发。C语言的设计目标是提供一种能以简易的方式编译、处理低级存储器、产生少量的机器码以及…...

如何搞定MySQL锁(全局锁、表级锁、行级锁)?这篇文章告诉你答案!太TMD详细了!!!

概述 锁是计算机协调多个进程或线程并发访问某一资源的机制。在数据库中&#xff0c;除传统的计算资源&#xff08;CPU、RAM、I/O&#xff09;的争用以外&#xff0c;数据也是一种供许多用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题&…...

云计算生态该怎么做?阿里云计算巢打了个样

2023 年 2 月 23 日至 24 日&#xff0c;由阿里云主办的「阿里云计算巢加速器」于杭州阿里云谷园区集结。 阿里云计算巢加速器于 2022 年 8 月正式启动招募&#xff0c;最终百奥利盟、极智嘉、EMQ、KodeRover、MemVerge 等 30 家创新企业入选计算加速器&#xff0c;覆盖了人工智…...

小樽C++ 多章⑧ (贰) 指针与数组

目录 1.C中数组变量名某些情况可以看成是指针 2.C语言的scanf 输入语句&#xff0c;printf 输出语句 3.用指针来当动态数组 小樽C 多章⑧ (壹) 指针变量https://blog.csdn.net/weixin_44775255/article/details/129031168 小樽C 多章⑧ (叁) 指针与字符串、(肆) 函数与指针…...

MXNet的机器翻译实践《编码器-解码器(seq2seq)和注意力机制》

机器翻译就是将一种语言翻译成另外一种语言&#xff0c;输入和输出的长度都是不定长的&#xff0c;所以这里会主要介绍两种应用&#xff0c;编码器-解码器以及注意力机制。编码器是用来分析输入序列&#xff0c;解码器用来生成输出序列。其中在训练时&#xff0c;我们会使用一些…...

RK3588平台开发系列讲解(同步与互斥篇)自旋锁介绍

平台内核版本安卓版本RK3588Linux 5.10Android 12文章目录 一、自旋锁介绍二、自旋锁相关的函数1、普通场景2、进程上下文和下半部3、中断相关三、相关结构体四、函数实现1、初始化2、获取自旋锁沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇介绍自旋锁的使用和基…...

Linux系统CPU占用率较高问题排查思路

作为工程师&#xff0c;在日常工作中我们会遇到 Linux服务器上出现CPU负载达到100%居高不下的情况&#xff0c;如果CPU 持续跑高&#xff0c;则会影响业务系统的正常运行&#xff0c;带来企业损失。对于CPU过载问题通常使用以下两种方式即可快速定位&#xff1a;方法一第一步&a…...

源码解析——HashMap

源码解析——HashMap1. 什么 是HashMap2. 为什么要使用HashMap3. HashMap的使用4. 源码解析4.1 关键问题4.1 存储结构4.2 HashMap 的容量和负载因子4.3 初始化过程4.3 put() 方法实现原理4.3.1 hash()4.3.2 resize()4.4 get() 方法实现原理5. 面试题总结6. ConcurrentHashmap(J…...

Elasticsearch 核心技术(六):内置的 8 种分词器详解 + 代码示例

❤️ 博客主页&#xff1a;水滴技术 &#x1f680; 支持水滴&#xff1a;点赞&#x1f44d; 收藏⭐ 留言&#x1f4ac; &#x1f338; 订阅专栏&#xff1a;大数据核心技术从入门到精通 文章目录一、内置分词器1. Standard&#xff08;标准分词器&#xff09;英文示例中文示例…...

Mysql8.0的特性

Mysql8.0的特性 建议使用8.0.17及之后的版本&#xff0c;更新的内容比较多。 新增降序索引 -- 如下所示&#xff0c;我们可以在创建索引时 在字段名后面指定desc进行降序排序 create table t1(c1 int,c2 int,index idx_c1_c2(c1,c2 desc));group by 不再隐式排序 mysql5.7的版…...

百度联盟做网站赚钱/推广方式有哪些

为什么80%的码农都做不了架构师&#xff1f;>>> 1.前言 前一篇我们讲述了HandlerMapping&#xff0c;它返回了请求对应的controller实例和对应的处理方法&#xff0c;很多读者认为就完事了&#xff0c;其实不然。所以我们需要这一片来讲述一下HandlerAdapter的作用…...

地推平台招代理/seo全国最好的公司

我们简单看一下pairs、ipairs以及自定义按key值顺序遍历的pairsBykeys这3中方式&#xff1a; -- table表的操作 function pairsBykeys(t)local a {}for n in pairs(t) doa[#a1] nendtable.sort(a)local i 0return function()i i 1return a[i], t[a[i]]end end -- table…...

唐山网站建设赫鸣科技/阿里云空间+1对1私人专属设计师

Python中的lambda函数 lambda函数是一种可以接受任意多个参数(包括可选参数)并返回单个表达式值的函数&#xff0c;它是python中快速定义单行函数的一种方式。 &#xff08;注&#xff1a;lambda后只能跟单行表达式&#xff09; 优点&#xff1a; 1、轻便&#xff0c;小巧 2…...

电子商务 网站建设/找客户资源的网站

本来以为不行,可是到nantcontrib 的Task Reference 一看,发现了 stcheckout 这个task 描述: Task to check out files from StarTeam repositories. 于是我按照例子: http://nantcontrib.sourceforge.net/help/tasks/stcheckout.html <property name"ST.url…...

科技公司网站首页/seo专员是什么

恶意吸费之后&#xff0c;你渴望开放还是开源&#xff1f;<?xml:namespace prefix o ns "urn:schemas-microsoft-com:office:office" />文 小刀马近期&#xff0c;有媒体报道Android身陷恶意扣费门。如果用户下载使用了这些内置恶意扣费的Android应用软件&…...

手机欧美视频网站模板下载 迅雷下载地址/目前引流最好的平台

日常生活中中&#xff0c;电路问题是比较重要的&#xff0c;尤其是现在家居中使用的电器越来越多&#xff0c;所以漏电保护器是一定要的&#xff0c;能有很好的保护作用&#xff0c;那漏电保护器原理是什么&#xff0c;又是如何接线的呢&#xff1f;漏电保护器接线方法以及注意…...