AQS 框架、JUC常见并发包 简述

news/2024/5/20 8:54:44 标签: java, juc, AQS

AQS(AbstractQueuedSynchronizer)是 Java 中的一个强大的同步框架,为我们提供了实现各种同步器的基础。在本篇博客中,我们将介绍 AQS 框架的基本原理,并探讨几个常见的 AQS 实现:ReentrantLock、CountDownLatch 和 Semaphore。我们将了解它们的区别以及各自的优缺点。

AQS__3">1. AQS 框架简介

AQS 是 Java 并发包中的核心部分,它提供了一个基于 FIFO(first in first out 先进先出)排队的双向链表等待队列,用于管理等待线程并控制资源的获取和释放。AQS 提供了一些核心的方法供子类继承和实现。下面我们重点介绍几个常见的 AQS 实现。

AQS__7">2. AQS 框架适用场景

AQS 用于单个服务内部的线程同步和并发控制。适用于所有单体架构,或者微服务项目中单个服务内部的线程同步和并发控制。不适用于分布式部署项目,对于集群部署的微服务项目,需要额外考虑跨服务的分布式同步问题,并选择适合的分布式锁解决方案来实现分布式环境下的同步与控制。例如 ZooKeeper、Redisson 等提供的分布式锁实现

3. ReentrantLock(可重入锁)

ReentrantLock 是 AQS 框架的一个常见应用,它提供了独占模式的锁。多个线程可以重复获取和释放该锁,而不会因为重复获取而发生死锁。这个特性使得 ReentrantLock 可以用于更复杂的同步场景。

ReentrantLock内部使用了一个int变量来表示锁的持有状态,作为条件是占用资源,值为0时表示未锁定状态,大于0表示已锁定。

下面是一个简单的示例代码:

import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockExample {
    private static ReentrantLock lock = new ReentrantLock();
    public static void main(String[] args) {
        lock.lock(); // 加锁,如果没有线程持有,则持有数加1返回。如果有线程持有,则持有数加1,并放到aqs等待队列排队
        try {
            // 执行保护的代码块
        } finally {
            lock.unlock(); // 释放锁,如果当前线程是此锁的持有者,则持有计数递减。如果保持计数现在为零,则释放锁。如果当前线程不是此锁的持有者,则抛出IllegalHonitorStateException
        }
    }
}

ReentrantLock 的优点是提供了可重入性,支持公平和非公平模式,并且具有更丰富的功能。缺点是相对于 synchronized 关键字有更高的复杂性和更多的开销。

4. CountDownLatch(倒计时门栓)

CountDownLatch 是一个同步机制,它允许一个或多个线程等待其他线程完成操作,它的构造器将会接收一个整数参数,表示需要等待的线程数量。

CountDownLatch内部也使用AQS的等待队列实现等待线程的阻塞,使用一个int类型的计数器来表示需要等待的线程数。

具体实现过程:

构造时将计数器初始化为传入的参数值。

countDown()方法会让计数器值减1,如果计数器变为0了,则唤醒等待队列中所有的线程

await()方法会首先将当前线程添加到AQS的等待队列中,然后判断计数器是否为0,如果不为0则一直等待。

import java.util.concurrent.CountDownLatch;
public class CountDownLatchExample {
    private static CountDownLatch latch = new CountDownLatch(3);
    public static void main(String[] args) throws InterruptedException {
        // 创建并启动多个线程
        for (int i = 0; i < 3; i++) {
            new Thread(() -> {
                // 执行操作
                latch.countDown(); // 操作完成后计数减1
            }).start();
        }
        latch.await(); // 等待计数为0
        // 所有操作完成后继续执行
    }
}

CountDownLatch 的优点是简单易用,可以很方便地实现等待其他线程完成的场景。缺点是一旦计数值确定后就无法修改,并且只能使用一次。

5. Semaphore(信号量)

Semaphore 是一种控制并发访问资源数量的同步工具。它允许多个线程同时访问共享资源,但需要限制总的访问线程数量。Semaphore 通过 AQS 的计数和等待机制来控制线程的获取和释放。

Semaphore内部也是通过一个int值表示许可数量来实现的;
acquire()方法会获取一个许可,如果没有就排队等待;
release()方法会释放一个许可。

下面是一个简单的示例代码:

import java.util.concurrent.Semaphore;
public class SemaphoreExample {
    private static Semaphore semaphore = new Semaphore(2);
    public static void main(String[] args) {
        // 创建并启动多个线程
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                try {
                    semaphore.acquire(); // 获取许可
                    // 访问共享资源
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    semaphore.release(); // 释放许可
                }
            }).start();
        }
    }
}

Semaphore 的优点是可以控制资源的并发访问数量,可以用于限流等场景。缺点是相对于其他同步机制有更多的复杂性和开销。

总结

锁定对象:

  • ReentrantLock是一种互斥锁,用于保护共享资源免受并发访问的侵害。
  • CountDownLatch和Semaphore不是用来进行同步互斥的,CountDownLatch用来协调并行任务。
  • Semaphore用来控制对共享资源的访问数量。

实现原理:

  • ReentrantLock使用状态变量表示锁的可重入性。
  • CountDownLatch使用状态计数器,计数到0为0后释放所有等待线程。
  • Semaphore使用许可证计数来控制可以访问的线程数量。

使用场景:

  • ReentrantLock用于同步访问的临界区。
  • CountDownLatch用于等待一组异步任务完成。
  • Semaphore用于控制对某种资源的并发数量。

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

相关文章

基于SSM的酒店客房预定管理系统

基于SSM的酒店客房预定管理系统的设计与实现~ 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringSpringMVCMyBatis工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 前台主页 客房详情 登录界面 管理员界面 用户界面 摘要 基于SSM&#xff08;…

PHP实现秒与时分秒互转

PHP实现秒与时分秒互转 具体代码如下 /** * 时间 H:i:s 转 s * User yaokai * param $his * return float|int */ function HisToS($his) { $str explode(:, $his); $len count($str); if ($len 3) { $time $str[0] * 3600 $str[1] * 60 $str[2]; …

初识JavaScript(一)

文章目录 一、JavaScript介绍二、JavaScript简介1.ECMAScript和JavaScript的关系2.ECMAScript的历史3.什么是Javascript&#xff1f;4.JavaScript的作用?5.JavaScript的特点 三、JavaScript基础1.注释语法2.JavaScript的使用 四、JavaScript变量与常量变量关键字var和let的区别…

odoo 按钮打印pdf报表

odoo打印一般是在动作里面进行的 所以此方法可用自定义按钮进行打印 <template id"report_sale_line_packing_template"> xxx </template><template id"report_sale_line_packing"><t t-call"web.basic_layout"><t …

香港服务器不稳定的几种情况

​  近年来&#xff0c;随着互联网的迅猛发展&#xff0c;香港作为一个重要的网络枢纽地区&#xff0c;扮演着连接中国内地和国际网络的重要角色。一些用户表示在使用香港服务器时可能会遇到不稳定的情况&#xff0c;导致访问困难、加载缓慢甚至无法连接。 为什么香港服务器会…

64T存储松下mov和索尼mp4文件变0字节恢复案例

64T存储松下mov和索尼mp4文件变0字节恢复案例 小型入门的小NAS凭借超市的性价比在各行业中开始流行&#xff0c;可以通过搭配普通SATA硬盘就可以完成阵列上线&#xff0c;部署也很简单&#xff0c;一根网线就搞定。我们看一个影视公司64T小NAS存储比较奇怪的恢复案例。 故障存…

Spring Boot实践 --windows环境下 K8s 部署 Docker

第一步&#xff1a;搭建项目并制作合适的jar包 这里我们准备好前面项目 用户管理系统 项目里的jar包。测试功能&#xff0c;定时任务会每过10s打印一次日志&#xff1a; E:\test>java -jar demospringboot-0.0.1-SNAPSHOT.jar2023-11-01 20:24:21.059 INFO 11848 --- [ …