AQS基本

news/2024/5/20 7:24:01 标签: java, juc

park和unpark

java">LockSupport.park();// 暂停当前线程
LockSupport.unpark(暂停线程对象);// 恢复某个线程的运行

unpark也可以在park之前调用,调用之后,对应的线程中如果后面执行了park,并不会停止
// 原理
每个线程都会关联一个Parker对象,Parker对象由三部分组成(_counter,_cond,_mutex)
调用park时:
_counter为0:则就进入_cond休息,将_counter再次置为0,线程停止运行
_counter为1,不需要休息,将_counter置为0,继续运行
调用unpark时:
线程在_cond中休息,将_counter变为1,将线程唤醒,线程将_counter变为0,继续向下运行
线程在运行,将_counter变为1,线程照常继续运行
java">Thread t1 = new Thread(() -> {
    log.debug("park...");
    LockSupport.park();// 让当前线程停下来
    log.debug("unpark...");
    log.debug("打断状态: {}", Thread.currentThread().isInterrupted());

    // 打断标记为真时,park无效,会继续向下运行
    LockSupport.park();
    log.debug("unpark...");
});
t1.start();

TimeUnit.SECONDS.sleep(1);
t1.interrupt();// 让park的线程恢复运行

AQS原理

ReentrantLock
CountDownLatch
ReentrantReadWriteLock
Semaphore
等都是基于AQS的

# AbstractQueuedSynchronizer.java
# 提供了一个模板用于实现(依赖于FIFO等待队列的阻塞锁和一些同步器)
# AQS解决了实现一个synchronizer的大量细节
# Provides a framework for implementing blocking locks and related synchronizers (semaphores, events, etc) that 
# rely on first-in-first-out (FIFO) wait queues.  

state属性表示资源的状态(分独占模式和共享模式),子类需要定义如何维护这个状态,控制如何获取锁和释放锁
独占模式:只能有一个线程访问资源
共享模式:可以有多个线程访问资源

AQS的实现依赖内部的双向队列,如果当前线程竞争锁失败,AQS会把当前线程以及等待状态信息封装成一个Node加入到同步队列中
同时再阻塞该线程,当获取锁的线程释放锁以后,会从队列中唤醒一个阻塞的节点(线程)

等待队列是"CLH"锁队列的一种变体
# The wait queue is a variant of a "CLH" lock queue.  
CLH锁通常用于自旋锁
# CLH locks are normally used for spinlocks.
相反,我们将它们用于阻塞同步器,但是使用相同的基本策略,将关于线程的一些控制信息保存在其节点的前任中
# We instead use them for blocking synchronizers, but use the same basic tactic of holding
# some of the control information about a thread in the predecessor of its node.
java">// 实现一个独占锁,需要子类实现
protected boolean tryAcquire(int arg) // state改为1,true表示加锁成功,将当前线程置为Owner线程
protected boolean tryRelease(int arg) // state改为0,Owner线程置为null
protected boolean isHeldExclusively()

// AQS中的acquire方法是加独占锁,内部会调用tryAcquire
// This method can be used to implement method {@link Lock#lock}.
public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}
// AQS中的release方法是释放独占锁,内部会调用tryRelease
// This method can be used to implement method {@link Lock#unlock}.
public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h); // 唤醒等待队列中线程
        return true;
    }
    return false;
}

自定义的不可重入独占锁

java">@Slf4j
public class Singleton{
    public static void main(String[] args) {
        MyLock lock = new MyLock();
        new Thread(() -> {
            lock.lock();
            try {
                log.debug("lock...");
                try {
                    TimeUnit.SECONDS.sleep(3);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } finally {
                log.debug("unlock...");
                lock.unlock();
            }
        },"t1").start();
        new Thread(() -> {
            lock.lock();
            try {
                log.debug("lock...");
            } finally {
                log.debug("unlock...");
                lock.unlock();
            }
        },"t2").start();
    }
}
final class MySync extends AbstractQueuedSynchronizer {
    @Override// 尝试获取锁
    protected boolean tryAcquire(int acquires) {
        if (acquires == 1){
            if (compareAndSetState(0, 1)) {// 将state置为1(原子性)
                setExclusiveOwnerThread(Thread.currentThread());// 将当前线程设置为owner线程
                return true;
            }
        }
        return false;
    }
    @Override// 尝试释放锁
    protected boolean tryRelease(int acquires) {
        if(acquires == 1) {
            if(getState() == 0) {
                throw new IllegalMonitorStateException();
            }
            setExclusiveOwnerThread(null);
            setState(0);
            return true;
        }
        return false;
    }
    protected Condition newCondition() {
        return new ConditionObject();
    }
    @Override// 是否持有锁
    protected boolean isHeldExclusively() {
        return getState() == 1;
    }
}
class MyLock implements Lock {
    static MySync sync = new MySync();
    @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.tryAcquireNanos(1, unit.toNanos(time));
    }
    @Override// 释放锁
    public void unlock() {
        sync.release(1);
    }
    @Override// 生成条件变量
    public Condition newCondition() {
        return sync.newCondition();
    }
}
java">// tryAcquireShared
尝试获取资源.负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源
//tryReleaseShared
尝试释放资源,成功则返回true,失败则返回 false

AQS详解
太难了,以后慢慢看吧


http://www.niftyadmin.cn/n/182346.html

相关文章

【C++】AVL树,红黑树

上一篇博客,我们深入讨论了map/multimap,set/multiset的使用和注意事项,让我们提到关联式容器,还有他们的底层都是AVL树实现的,那么本文带你深入解析AVL和红黑树 注意:本篇博客在代码部分,强烈建…

Docker 镜像使用

目录 1、列出镜像列表 2、获取一个新的镜像 3、查找镜像 4、拖取镜像 5、删除镜像 6、创建镜像 a.更新镜像 b.构建镜像 设置镜像标签 当运行容器时,使用的镜像如果在本地中不存在,docker 就会自动从 docker 镜像仓库中下载,默认是从 …

CentOS8提高篇13:Linux最小化安装后一般需要安装的命令的rpm包合集

1、ifconfig命令 yum install net-tools -y 2、vim编辑器 yum -y install vim 3、tab补全键 安装epel 源 yum -y install epel-release 4、加快yun速度 yum -y install yum-plugin-fastestmirror 5、安装bash-completion yum -y install bash-completion 立即生效 …

Vue表单双绑、组件

表单双绑、组件 1、什么是双向数据绑定 ​ Vue.js是一个MVVM框架,即数据双向绑定,即当数据发生变化的时候,视图也就发生变化,当视图发生变化的时候,数据也会跟着同步变化。这也算是Vue.js的精髓之处了。 ​ 值得注意的是,我们所…

【新2023Q2模拟题JAVA】华为OD机试 - 洞穴探险 or 最远足迹

最近更新的博客 华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为od机试,独家整理 已参加机试人员的实战技巧本篇题解:洞穴探险 or 最远足迹 题目 …

软件测试模型有几种?这4中软件测试模型你都知道吗

在软件开发过程中,人们根据经验教训并结合未来软件的发展趋势总结出了很多软件开发模型,如瀑布模型、快速原型模型、迭代模型等,这些模型对软件开发过程具有很好的指导作用,但遗憾的是它们对软件测试并没有给予足够的重视&#xf…

【Ruby学习笔记】1.Ruby的简介、环境、Linux安装及Windows安装

前言 本章介绍Ruby的简介、环境、Linux安装及Windows安装。 Ruby 简介 Ruby是一种纯粹的面向对象编程语言。它由日本的松本行弘(まつもとゆきひろ/Yukihiro Matsumoto)创建于1993年。 您可以在 www.ruby-lang.org 的 Ruby 邮件列表上找到松本行弘&am…

Git统计项目成员代码提交量

目录 一、前言 二、Git Bash 三、统计信息 1.统计某项目中成员数量 2.统计所有用户的提交总次数 3.统计所有用户指定时间段的提交次数 4.按用户名统计提交次数 完整脚本如下 一、前言 项目中有很多成员,如何查看各个时间段每个组员的代码提交量,下…