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

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

1. AQS 框架简介

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

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用于控制对某种资源的并发数量。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值