Java基础-抽象队列同步器:AbstractQueuedSynchronizer(1)

简介: AQS定义了一套多线程访问共享资源的同步器框架。许多同步类实现都依赖于它,如常用的ReentrantLock/Semaphore/CountDownLatch。它维护了一个volatile int state(代表共享资源)和一个FIFO线程等待队列。多线程争用资源被阻塞时会进入此队列。AQS定义两种资源共享方式:Exclusive(独占,只有一个线程能执行,如ReentrantLock)和Share(共享,多个线程可同时执行,如Semaphore/CountDownLatch)。

简介

AQS定义了一套多线程访问共享资源的同步器框架。
许多同步类实现都依赖于它,如常用的ReentrantLock/Semaphore/CountDownLatch。
它维护了一个volatile int state(代表共享资源)和一个FIFO线程等待队列。
多线程争用资源被阻塞时会进入此队列。
AQS定义两种资源共享方式:Exclusive(独占,只有一个线程能执行,如ReentrantLock)和Share(共享,多个线程可同时执行,如Semaphore/CountDownLatch)。

自定义同步器

不同的自定义同步器争用共享资源的方式也不同。
自定义同步器在实现时只需要实现共享资源state的获取与释放方式即可;
至于具体线程等待队列的维护(如获取资源失败入队/唤醒出队等),AQS已经在顶层实现好了。
自定义同步器实现时主要实现以下几种方法:

isHeldExclusively:该线程是否正在独占资源。只有用到condition才需要去实现它。
tryAcquire:独占方式。尝试获取资源,成功则返回true,失败则返回false。
tryRelease:独占方式。尝试释放资源,成功则返回true,失败则返回false。
tryAcquireShared:共享方式。尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。
tryReleaseShared:共享方式。尝试释放资源,如果释放后允许唤醒后续等待结点返回true,否则返回false。

ReentrantLock

state初始化为0,表示未锁定状态。
线程lock()时,会调用tryAcquire()独占该锁并将state+1。
其他线程再tryAcquire()时就会失败。
直到A线程unlock()到state=0(即释放锁)为止,其它线程才有机会获取该锁。
当然,释放锁之前,线程自己是可以重复获取此锁的(state会累加),这就是可重入的概念。
要注意,获取多少次就要释放多么次,这样才能保证state是能回到零态的。

CountDownLatch

任务分为N个子线程去执行,state也初始化为N(注意N要与线程个数一致)。
这N个子线程是并行执行的,每个子线程执行完后countDown()一次,state会CAS减1。
等到所有子线程都执行完后(即state=0),会unpark()主调用线程。
然后主调用线程就会从await()函数返回,继续后余动作。

Semaphore

是用来控制同时访问特定资源的线程数量,通过协调各个线程以保证合理地使用公共资源。
Semaphore通过使用计数器来控制对共享资源的访问。
如果计数器大于0,则允许访问。 如果为0,则拒绝访问。
计数器所计数的是允许访问共享资源的许可。 因此,要访问资源,必须从信号量中授予线程许可。
信号量控制登录用户,示例代码:

// 登录用户信号量
static final int MAX_AVAILABLE = 10;
static final Semaphore LOGIN_SEMAPHORE = new Semaphore(MAX_AVAILABLE, false);

// 线程池初始化
static ExecutorService executorService = Executors.newFixedThreadPool(10);

public static void main(String[] args) {
    // 连续登录十个用户
    for(int i = 0; i < 10; i++) {
        final int num = i;
        executorService.execute(() -> {
            if(LOGIN_SEMAPHORE.tryAcquire()) {
                System.out.println("用户:" + num + " 登录成功");
            } else {
                System.out.println("用户:" + num + " 登录失败");
            }
        });
    }
    executorService.shutdown();
    
    // 第十一个用户登录
    if(LOGIN_SEMAPHORE.tryAcquire()) {
        System.out.println("登录成功");
    } else {
        System.out.println("登录用户已满,登录失败");
    }
    
    // 有用户退出
    LOGIN_SEMAPHORE.release();
    
    // 第十二个用户登录
    if(LOGIN_SEMAPHORE.tryAcquire()) {
        System.out.println("登录成功");
    } else {
        System.out.println("登录用户已满,登录失败");
    }
}
相关文章
|
2月前
|
存储 消息中间件 安全
JUC组件实战:实现RRPC(Java与硬件通过MQTT的同步通信)
【10月更文挑战第9天】本文介绍了如何利用JUC组件实现Java服务与硬件通过MQTT的同步通信(RRPC)。通过模拟MQTT通信流程,使用`LinkedBlockingQueue`作为消息队列,详细讲解了消息发送、接收及响应的同步处理机制,包括任务超时处理和内存泄漏的预防措施。文中还提供了具体的类设计和方法实现,帮助理解同步通信的内部工作原理。
JUC组件实战:实现RRPC(Java与硬件通过MQTT的同步通信)
|
2月前
|
存储 缓存 Java
java基础:IO流 理论与代码示例(详解、idea设置统一utf-8编码问题)
这篇文章详细介绍了Java中的IO流,包括字符与字节的概念、编码格式、File类的使用、IO流的分类和原理,以及通过代码示例展示了各种流的应用,如节点流、处理流、缓存流、转换流、对象流和随机访问文件流。同时,还探讨了IDEA中设置项目编码格式的方法,以及如何处理序列化和反序列化问题。
83 1
java基础:IO流 理论与代码示例(详解、idea设置统一utf-8编码问题)
|
1月前
|
Java 调度
Java 线程同步的四种方式,最全详解,建议收藏!
本文详细解析了Java线程同步的四种方式:synchronized关键字、ReentrantLock、原子变量和ThreadLocal,通过实例代码和对比分析,帮助你深入理解线程同步机制。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
Java 线程同步的四种方式,最全详解,建议收藏!
|
2月前
|
安全 Java 开发者
Java多线程中的`wait()`、`notify()`和`notifyAll()`方法,探讨了它们在实现线程间通信和同步中的关键作用
本文深入解析了Java多线程中的`wait()`、`notify()`和`notifyAll()`方法,探讨了它们在实现线程间通信和同步中的关键作用。通过示例代码展示了如何正确使用这些方法,并分享了最佳实践,帮助开发者避免常见陷阱,提高多线程程序的稳定性和效率。
38 1
|
2月前
|
存储 安全 Java
【用Java学习数据结构系列】探索栈和队列的无尽秘密
【用Java学习数据结构系列】探索栈和队列的无尽秘密
32 2
|
3月前
|
安全 Java API
【Java面试题汇总】Java基础篇——String+集合+泛型+IO+异常+反射(2023版)
String常量池、String、StringBuffer、Stringbuilder有什么区别、List与Set的区别、ArrayList和LinkedList的区别、HashMap底层原理、ConcurrentHashMap、HashMap和Hashtable的区别、泛型擦除、ABA问题、IO多路复用、BIO、NIO、O、异常处理机制、反射
【Java面试题汇总】Java基础篇——String+集合+泛型+IO+异常+反射(2023版)
|
3月前
|
Java API 容器
JAVA并发编程系列(10)Condition条件队列-并发协作者
本文通过一线大厂面试真题,模拟消费者-生产者的场景,通过简洁的代码演示,帮助读者快速理解并复用。文章还详细解释了Condition与Object.wait()、notify()的区别,并探讨了Condition的核心原理及其实现机制。
|
2月前
|
存储 算法 Java
【用Java学习数据结构系列】用堆实现优先级队列
【用Java学习数据结构系列】用堆实现优先级队列
32 0
|
存储 算法 安全
【Java 数据结构及算法实战】系列 014:Java队列08——数组实现的双端队列ArrayDeque
【Java 数据结构及算法实战】系列 014:Java队列08——数组实现的双端队列ArrayDeque
168 0
【Java 数据结构及算法实战】系列 014:Java队列08——数组实现的双端队列ArrayDeque
|
存储 算法 安全
【Java数据结构及算法实战】系列012:Java队列06——数组实现的优先级阻塞队列PriorityBlockingQueue
【Java数据结构及算法实战】系列012:Java队列06——数组实现的优先级阻塞队列PriorityBlockingQueue
143 0