AQS的应用:基于AQS实现自定义同步器

简介: AQS的应用:基于AQS实现自定义同步器

三、基于AQS实现自定义同步器


之前学习了这么多关于AQS的原理性的知识,这一期,我们来基于AQS实现一个不可重入的独占锁, 自定义AQS需要重写一系列函数,还需要定义原子变量state的含义。这里我们定义, state=0 表示目前锁没有被线程持有 ,state=1 表示锁己经被某一个线程持有。 由于是不可重入锁,所以不需要记录持有锁的线程获取锁的次数。另外,还要自定义锁的支持条件变量。


1、代码实现


如下代码是基于AQS实现不可重入的独占锁。

package MyNonReentrantLock;
import java.io.Serializable;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
public class NonReentrantLock implements Lock, Serializable {
    //静态内部类,用于辅助
    private static class Sync extends AbstractQueuedSynchronizer{
        @Override
        protected boolean tryAcquire(int arg) {
           assert arg == 1;//如果state为0,则尝试获取锁
            if (compareAndSetState(0,1)){
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }
        @Override
        protected boolean tryRelease(int arg) {
            assert arg == 1;//如果state为0,则尝试获取锁
            if (getState()==0){
                throw new IllegalMonitorStateException();
            }
            setExclusiveOwnerThread(null);
            setState(0);
            return true;
        }
        @Override
        protected boolean isHeldExclusively() {
            // 是否锁已经被持有
            return getState()==1;
        }
        //提供条件变量接口
        public Condition newCondition(){
            return new ConditionObject();
        }
    }
    Sync sync = new Sync();
    @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();
    }
}

在如上代码中, NonReentrantLock 定义了一个内部类 Sync 用来实现具体的锁的操作, Sync 继承了AQS 。由于实现的是独占模式的锁,所以Sync重写了tryAcquire/tryRelease/isHeldExclusively个方法。另 外, Sync 提供 newCondition 这个方法用来支持条件变量。


2、使用自定义锁实现生产一消费模型


代码如下:

package MyNonReentrantLock;
import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.locks.Condition;
public class 自定义生产消费模型 {
    static NonReentrantLock lock = new NonReentrantLock();
    static Condition notFull = lock.newCondition();
    static Condition notEmpty = lock.newCondition();
    static Queue<String> queue = new LinkedBlockingQueue<>();
    static int queueSize = 10;
    public static void main(String[] args) {
        Thread producer = new Thread(() -> {
            lock.lock();
            try {
                //如果队列满了,则等待
                while (queue.size() == queueSize) {
                    notEmpty.await();
                }
                //添加队列元素
                queue.add("element ");
                //唤醒消费线程
                notFull.signalAll();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                //释放锁
                lock.unlock();
            }
        });
        Thread consumer = new Thread(() -> {
            lock.lock();
            try {
                //队列为空,则等待
                while (queue.size()==0){
                    notFull.await();
                }
                //消费元素
                queue.poll();
                //唤醒生产线程
                notEmpty.signalAll();
            }catch (InterruptedException e){
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        });
        producer.start();
        consumer.start();
    }
}

如上代码首先创建了NonReentrantLock的一个对象lock ,然后调用 lock.newCondition创建了两个条件变量,用来进行生产者和消费者线程之间的同步。


在 main 函数里面,首先创建了 producer 生产线程,在线程内部首先调用 lock.lock()获取独占锁,然后判断当前队列是否己经满了 ,如果满了则调用 notEmpty.await()阻塞挂起当前线程。需要注意的是,这里使用 while 而不是 if 是为了避免虚假唤醒 。如果队列不满则直接向队列里面添加元素,然后调用 notFull.signalAll唤醒所有因为消费元素而被阻塞的消费线程,最后释放获取的锁。


然后在 main 函数里面创建了 consumer 生产线程,在线程内部首先调用 lock.lock()获取独占锁,然后判断当前队列里面是不是有元素,如果队列为空则调用 notFull.await()阻塞挂起当前线程。需要注意的是,这里使用 while 而不 if 是为了避免虚假唤醒。如果队列不为空则直接从队列里面获取并移除元素,然后唤醒因为队列满而被阻塞的生产线程,最后释放获取的锁。


到目前为止,AQS相关的知识就告一段落,后续的多线程学习中我们还是会继续看到AQS的影子!我是Zhongger,一个在互联网行业摸鱼写代码的打工人,你们的【关注】和【在看】与支持是我创作的最大动力,我们下期见~

相关文章
|
Java 编译器
Java“精度可能丢失”错误解决
在处理Java编程语言中“精度可能丢失”的警告或错误信息时,通常涉及到数据类型之间的转换,特别是从高精度类型(如long、double)转换到低精度类型(如int、short)时。本指南将帮助你理解这一问题的根源,并提供有效策略来避免或解决此类错误,确保程序正确无误地运行。我们将会探讨如何使用显式类型转换(cast),以及如何优化代码逻辑来规避潜在的数据丢失风险。
906 0
|
JSON fastjson 数据格式
fastjson中的一些常用方法推荐
fastjson中的一些常用方法推荐
318 0
|
Java 数据安全/隐私保护 Spring
SpringSecurity 权限管理的实现
SpringSecurity是一个权限管理框架,核心是认证和授权,前面介绍过了认证的实现和源码分析,本文重点来介绍下权限管理这块的原理。
387 0
|
3月前
|
人工智能 JavaScript 编译器
AI工具的“超级外挂”:从零手把手教你搭建私人 MCP 服务器
本文手把手教你用Node.js从零搭建私人MCP(模型上下文协议)服务器,解决AI无法直接访问本地文件、数据库等痛点。含环境配置、TypeScript编译避坑、Hello World工具开发及Inspector调试全流程,助你赋予AI真实行动力!
1198 1
AI工具的“超级外挂”:从零手把手教你搭建私人 MCP 服务器
|
SQL 分布式计算 大数据
一张图,详解大数据技术架构
一张图,详解大数据技术架构
|
分布式计算 Hadoop Linux
Centos7配置Hadoop出现Permission denied (publickey,gssapi-keyex,gssapi-with-mic,password)的解决
Centos7配置Hadoop出现Permission denied (publickey,gssapi-keyex,gssapi-with-mic,password)的解决
2637 0
|
12月前
|
运维 网络协议 Go
Go网络编程:基于TCP的网络服务端与客户端
本文介绍了使用 Go 语言的 `net` 包开发 TCP 网络服务的基础与进阶内容。首先简述了 TCP 协议的基本概念和通信流程,接着详细讲解了服务端与客户端的开发步骤,并提供了简单回显服务的示例代码。同时,文章探讨了服务端并发处理连接的方法,以及粘包/拆包、异常检测、超时控制等进阶技巧。最后通过群聊服务端的实战案例巩固知识点,并总结了 TCP 在高可靠性场景中的优势及 Go 并发模型带来的便利性。
LLM用于时序预测真的不行,连推理能力都没用到
【7月更文挑战第15天】LLM在时序预测上的应用遇挫:研究显示,大型语言模型在多个实验中未显优势,甚至被简单注意力层替代时效果不变或更好。预训练知识未能有效利用,处理时序依赖性不足,且在小样本学习中未见提升。[链接:](https://arxiv.org/pdf/2406.16964)**
456 2
|
机器学习/深度学习 人工智能 分布式计算
编程语言未来发展趋势探析:简化与标准化、并发与分布式、智能应用新篇章
编程语言未来发展趋势探析:简化与标准化、并发与分布式、智能应用新篇章
466 2
|
消息中间件 存储 负载均衡
高并发流量杀手锏:揭秘秒杀系统背后的削峰技术!
本文介绍了秒杀场景下的“削峰填谷”策略,通过消息队列缓冲用户请求,避免高并发对系统造成冲击。文中详细解释了消息队列的工作原理及如何通过预扣减库存和分布式锁确保数据一致性,同时还提出了合理的消息队列配置、高可用性及数据库负载均衡等最佳实践。通过这些技术手段,可有效提升系统的稳定性和用户体验。
1065 8
高并发流量杀手锏:揭秘秒杀系统背后的削峰技术!