AQS原理及用法_aqs是什么意思

AQS原理及用法_aqs是什么意思AQS原理及用法1AQS简介AQS全称为AbstractQueuedSynchronizer,是Java中的一个抽象类。AQS是一个用于构建锁、同步器、协作工具类的工具类(框架)。有了AQS之后,更多的协作工具类都可以方便得被写出来。有了AQS,构建线程协作类就容易多了。控制并发流程的类,都需要线程等待和唤醒的功能,这是这些类的共同特点,因此可以抽象出一个基类,这就是AQS。AQS广泛用于控制并发流程的类,如下图:其中Sync是这些类中都有的内部类,其结构如下:

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全家桶1年46,售后保障稳定

AQS原理及用法

1 AQS简介

  • AQS全称为AbstractQueuedSynchronizer,是Java中的一个抽象类。

  • AQS是一个用于构建锁、同步器、协作工具类的工具类(框架)。有了AQS之后,更多的协作工具类都可以方便得被写出来。

  • 有了AQS,构建线程协作类就容易多了。

  • 控制并发流程的类,都需要线程等待和唤醒的功能,这是这些类的共同特点,因此可以抽象出一个基类,这就是AQS

  • AQS广泛用于控制并发流程的类,如下图:

在这里插入图片描述

  • 其中Sync是这些类中都有的内部类,其结构如下:

在这里插入图片描述

  • 可以看到:SyncAQS的实现。

  • AQS主要完成的任务:

    • (1)同步状态(比如说计数器)的原子性管理;

    • (2)线程的阻塞和解除阻塞;

    • (3)队列的管理。

2 AQS原理

  • AQS最核心的就是三大部分:

    • 状态:state;

    • 控制线程抢锁和配合的FIFO队列(双向链表);

    • 期望协作工具类去实现的获取/释放等重要方法(重写)。


状态state

  • 这里state的具体含义,会根据具体实现类的不同而不同:比如在Semapore里,他表示剩余许可证的数量;在CountDownLatch里,它表示还需要倒数的数量;在ReentrantLock中,state用来表示“锁”的占有情况,包括可重入计数,当state的值为0的时候,标识该Lock不被任何线程所占有。

  • state是volatile修饰的,并被并发修改,所以修改state的方法都需要保证线程安全,比如getState、setState以及compareAndSetState操作来读取和更新这个状态。这些方法都依赖于unsafe类。

FIFO队列

  • 这个队列用来存放“等待的线程,AQS就是“排队管理器”,当多个线程争用同一把锁时,必须有排队机制将那些没能拿到锁的线程串在一起。当锁释放时,锁管理器就会挑选一个合适的线程来占有这个刚刚释放的锁。

  • AQS会维护一个等待的线程队列,把线程都放到这个队列里,这个队列是双向链表形式。

实现获取/释放等方法

  • 这里的获取和释放方法,是利用AQS的协作工具类里最重要的方法,是由协作类自己去实现的,并且含义各不相同;

  • 获取方法:获取操作会以来state变量,经常会阻塞(比如获取不到锁的时候)

    • 在Semaphore中,获取就是acquire方法,作用是获取一个许可证;

    • 而在CountDownLatch里面,获取就是await方法,作用是等待,直到倒数结束;

  • 释放方法

    • 在Semaphore中,释放就是release方法,作用是释放一个许可证;

    • 在CountDownLatch里面,获取就是countDown方法,作用是将倒数的数减一;

  • 需要每个实现类重写tryAcquire和tryRelease等方法。

3 AQS在juc中的应用

AQS在juc中用法套路:

  • 第一步:写一个类:想好协作的逻辑,实现获取/释放方法;
  • 第二步:类的内部写一个Sync类,继承AbstractQueuedSychronizer;
  • 第三步:根据是否独占来重写 tryAcquire/tryRelease 或 tryAcquireShared(int acquires) /tryReleaseShared(int release) 等方法,在之前写的获取/释放方法中调用、AQS的 acquire/release 或者 acquireShared/releaseShared 方法。
  • 为了方便,下面分析中的AbstractQueuedSynchronizer类都用AQS代替。

3.1 AQS在CountDownLatch中的应用

  • 当我们新建一个CountDownLatch对象时:
CountDownLatch cdl = new CountDownLatch(3);

Jetbrains全家桶1年46,售后保障稳定

  • 会在cdl内部创建一个Sync对象,并将this.sync中的state设置为3,代表要倒数三次。

  • 当我们调用cdl.await(),此时调用该语句的线程会陷入等待,原因分析如下:

  • await()方法中的内容如下:

public void await() throws InterruptedException { 
     // 位于CountDownLatch类中
    sync.acquireSharedInterruptibly(1);
}
  • acquireSharedInterruptibly位于AQS类中,内容如下:
public final void acquireSharedInterruptibly(int arg)
    throws InterruptedException { 
     // 位于AQS类中
    
    if (Thread.interrupted()) throw new InterruptedException();
    
    if (tryAcquireShared(arg) < 0) 
        doAcquireSharedInterruptibly(arg);  // 把当前线程放入队列等待,并且让当前线程阻塞
}
  • tryAcquireShared(arg)AQS的子类Sync重写,内容如下:
protected int tryAcquireShared(int acquires) { 
     // 位于CountDownLatch类的内部类Sync中
    return (getState() == 0) ? 1 : -1;
}
  • getState()位于AQS类中,内容如下:
protected final int getState() { 
     // 位于AQS类中
    return state;
}
  • 因为getState()返回的是 3,因此tryAcquireShared会返回 -1,因此会执行doAcquireSharedInterruptibly,然后当前线程进入阻塞。

  • 当调用countDown()函数,会让state--,如果值变为0,则会唤醒之前等待的线程。原因分析如下:

  • countDown()方法中的内容如下:

public void countDown() { 
     // 位于CountDownLatch类中
    sync.releaseShared(1);
}
  • releaseShared位于AQS类中,内容如下:
public final boolean releaseShared(int arg) { 
     // 位于AQS类中
    if (tryReleaseShared(arg)) { 
   
        doReleaseShared();  // 把之前阻塞的线程全部唤醒
        return true;
    }
    return false;
}
  • tryReleaseShared(arg)AQS的子类Sync重写,内容如下:
protected boolean tryReleaseShared(int releases) { 
     // 位于CountDownLatch类的内部类Sync中
    // Decrement count; signal when transition to zero
    for (;;) { 
   
        int c = getState();
        if (c == 0)
            return false;
        int nextc = c-1;
        if (compareAndSetState(c, nextc))
            return nextc == 0;
    }
}
  • 可以看到当state的值减少到 0 之后会唤醒之前阻塞进程。

3.1 AQS在Semaphore中的应用

  • 当我们新建一个Semaphore对象时:
Semaphore s = new Semaphore(3);
  • 会在s内部创建一个NonfairSync对象(继承自Sync),并将this.sync中的state设置为3,代表有三个通行证。

  • 当我们调用s.acquire(2),会获取到两个许可证,分析如下:

  • acquire(int permits)方法中的内容如下:

public void acquire(int permits) throws InterruptedException { 
     // 位于Semaphore类中
    if (permits < 0) throw new IllegalArgumentException();
    sync.acquireSharedInterruptibly(permits);
}
  • acquireSharedInterruptibly位于AQS类中,内容如下:
public final void acquireSharedInterruptibly(int arg)
    throws InterruptedException { 
     // 位于AQS类中
    
    if (Thread.interrupted()) throw new InterruptedException();
    
    if (tryAcquireShared(arg) < 0) 
        doAcquireSharedInterruptibly(arg);  // 把当前线程放入队列等待,并且让当前线程阻塞
}
  • tryAcquireShared(arg)AQS的子类Sync重写,内容如下:
protected int tryAcquireShared(int acquires) { 
     // 位于Semaphore类的内部类NonfairSync中
    return nonfairTryAcquireShared(acquires);
}
  • nonfairTryAcquireShared内容如下:
final int nonfairTryAcquireShared(int acquires) { 
     // 位于Semaphore类的内部类Sync中
    for (;;) { 
   
        int available = getState();
        int remaining = available - acquires;
        if (remaining < 0 ||
            compareAndSetState(available, remaining))
            return remaining;
    }
}
  • 上面的available就是state,当我们想要获取的许可证数量acquires小于等于state时,返回正数,在函数acquireSharedInterruptibly判断不成立,不会陷入阻塞,并将state更新为最新的值;否则如果acquires大于state时,返回负数,当前线程会陷入阻塞。

  • 当我们调用s.release(2),会获取到两个许可证,分析如下:

  • release(int permits)方法中的内容如下:

public void release(int permits) { 
     // 位于Semaphore类中
    if (permits < 0) throw new IllegalArgumentException();
    sync.releaseShared(permits);
}
  • releaseShared位于AQS类中,内容如下:
public final boolean releaseShared(int arg) { 
     // 位于AQS类中
    if (tryReleaseShared(arg)) { 
   
        doReleaseShared();  // 把之前阻塞的线程全部唤醒
        return true;
    }
    return false;
}
  • tryAcquireShared(arg)AQS的子类Sync重写,内容如下:
protected final boolean tryReleaseShared(int releases) { 
     // 位于Semaphore类的内部类Sync中
    for (;;) { 
   
        int current = getState();
        int next = current + releases;
        if (next < current) // overflow
            throw new Error("Maximum permit count exceeded");
        if (compareAndSetState(current, next))
            return true;
    }
}
  • 该函数会将state的值增加releases,也就是增加许可证的数量。

3.1 AQS在ReentrantLock中的应用

  • 当我们新建一个ReentrantLock对象时:
ReentrantLock r = new ReentrantLock();
  • 会在r内部创建一个NonfairSync对象sync(继承自Sync)。

  • 当我们调用r.lock(),分析如下:

  • r.lock()方法中的内容如下:

public void lock() { 
     // 位于ReentrantLock类中
    sync.lock();
}
  • sync.lock()内容如下:
final void lock() { 
     // 位于ReentrantLock类的内部类NonfairSync中
    if (compareAndSetState(0, 1))  // CAS只有在没有人持有这把锁的时候才能成功
        setExclusiveOwnerThread(Thread.currentThread());  // 把当前线程设置为持有锁的线程
    else
        acquire(1);
}
  • acquire(1)位于AQS类中,内容如下:
public final void acquire(int arg) { 
     // 位于AQS类中
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}
  • tryAcquire内容如下:
protected final boolean tryAcquire(int acquires) { 
     // 位于ReentrantLock类的内部类NonfairSync中
    return nonfairTryAcquire(acquires);
}
  • nonfairTryAcquire内容如下:
final boolean nonfairTryAcquire(int acquires) { 
   
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) { 
   
        if (compareAndSetState(0, acquires)) { 
   
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    else if (current == getExclusiveOwnerThread()) { 
   
        int nextc = c + acquires;
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}
  • 由该函数可以看出,当state为0时,代表没有任何人持有该锁,获取该锁;否则如果当前线程正好是该锁的持有者,重入。

  • 当我们调用r.unlock(),分析如下:

  • r.unlock()方法中的内容如下:

public void unlock() { 
     // // 位于ReentrantLock类中
    sync.release(1);
}
  • sync.release(1)内容如下:
public final boolean release(int arg) { 
     // 位于AQS类中
    if (tryRelease(arg)) { 
   
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}
  • tryReleaseAQS的子类Sync重写,内容如下:
protected final boolean tryRelease(int releases) { 
     // 位于ReentrantLock类的内部类Sync中
    int c = getState() - releases;  // 将重入次数减少releases
    if (Thread.currentThread() != getExclusiveOwnerThread())  // 判断当前线程是不是持有锁的线程
        throw new IllegalMonitorStateException();
    boolean free = false;
    if (c == 0) { 
     // 只有当重入次数为0时,才去释放锁
        free = true;
        setExclusiveOwnerThread(null);  // 设置当前持有这把锁的线程为null
    }
    setState(c);
    return free;
}
  • 可以看到state的值被减去releases,如果state变为0,则设置当前持有这把锁的线程为null。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:http://www.javaforall.cn/234952.html原文链接:http://www.javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • 《深入理解mybatis原理》 MyBatis缓存机制的设计与实现

    《深入理解mybatis原理》 MyBatis缓存机制的设计与实现本文主要讲解MyBatis非常棒的缓存机制的设计原理,给读者们介绍一下MyBatis的缓存机制的轮廓,然后会分别针对缓存机制中的方方面面展开讨论。

  • 预训练模型还要训练吗_多模态预训练模型

    预训练模型还要训练吗_多模态预训练模型若使用已保存好的镜像reid_mgn:v1,在本机上可按如下操作训练#1.进入已保存环境的镜像(reid_mgn:v1(8.48G)、pytorch/pytorch:1.0.1-cuda10.0-cudnn7-devel_mgnreid(6.37G))nvidia-dockerrun-it–rm-v/home/lc-deep/sdr:/home/personReID…

  • java发送邮件代码[通俗易懂]

    java发送邮件代码[通俗易懂]java发送邮件首先需要通过对应的邮件服务转发到中间基站,再有接收方服务器接收邮件,转发给收件人,因此我们再发送邮件时需要先设定邮件发出服务(例如qq、网易等),在配置邮件发送协议以及发送人和接收人,最后设置邮件内容,此处我以正文中带有图片的代码为例!编写代码前,我们需要准备javax下的mail包和activation包。这个在文章的末位会提供下载地址,好了天冷屁股凉,我们直接上高速。。importjavax.mail.Authenticator;importjavax.mail.Mess

  • 项目范围管理知识领域共有六个过程_项目范围管理的主要内容

    项目范围管理知识领域共有六个过程_项目范围管理的主要内容项目范围管理项目范围管理包括确保项目做且只做所需的全部工作,以成功完成项目的各个过程。管理项目范围主要在于定义和控制哪些工作应该包括在项目内,哪些不应该包括在项目内项目范围管理的各个过程,包括:5.1规划范围管理一创建范围管理计划,书面描述将如何定义、确认和控制项目范围的过程。5.2收集需求—一为实现项目目标而确定、记录并管理干系人的需要和需求的过程5.3定义范围——制定项目和产品详细描述…

  • Android 中文 API (29) —— CompoundButton[通俗易懂]

    Android 中文 API (29) —— CompoundButton[通俗易懂]前言  本章内容是android.widget.CompoundButton,翻译来自德罗德,再次感谢德罗德!期待你一起参与AndroidAPI的中文翻译,联系我over140@gmail.com。 声明  欢迎转载,但请保留文章原始出处:)    博客园:http://www.cnblogs.com/    Android中文翻译组:http://www.cnblogs.com…

  • Vue简明实用教程(01)——Vue框架入门

    Vue简明实用教程(01)——Vue框架入门Vue是一个渐进式的JavaScript框架。Vue主要特征如下:

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号