首页 > 其他分享 >一文彻底弄懂JUC工具包的Semaphore

一文彻底弄懂JUC工具包的Semaphore

时间:2024-11-10 18:08:31浏览次数:1  
标签:JUC 许可 int acquire 工具包 线程 Semaphore release

Semaphore 是 Java 并发包 (java.util.concurrent) 中的重要工具,主要用于控制多线程对共享资源的并发访问量。它可以设置“许可证”(permit)的数量,并允许指定数量的线程同时访问某一资源,适合限流、资源池等场景。下面从源码设计、底层原理、应用场景、以及与其它 JUC 工具的对比来详细剖析 Semaphore。

一、Semaphore 的基本原理

Semaphore 本质上是一种计数信号量,内部维护一个许可计数,每个线程在进入时需要申请一个许可(acquire),完成后释放该许可(release)。当许可计数为零时,其他线程会阻塞,直到有线程释放许可。

1. 计数和许可

  • 许可数:Semaphore 在初始化时设置许可数,通常表示可以同时访问资源的线程数量。
  • 计数增减:调用 acquire() 时减少许可数,调用 release() 时增加许可数。
  • 公平性:Semaphore 可以设置为“公平”模式,保证线程按 FIFO 顺序获得许可。默认是“非公平”模式,性能更高,但线程获取许可的顺序不保证。

二、底层实现与源码解析

Semaphore 基于 AbstractQueuedSynchronizer (AQS) 实现,其实现方式和 CountDownLatch 类似,但使用了 AQS 的共享模式,并对许可计数进行精确管理。

1. AQS 的共享模式

Semaphore 使用 AQS 的共享模式(Shared),其中 AQS 的 state 表示剩余许可数。acquireShared() 方法用于申请许可,releaseShared() 方法用于释放许可。

2. 内部类 Sync 的实现

Semaphore 的核心实现依赖于其内部类 Sync,Sync 是 AbstractQueuedSynchronizer 的子类。根据是否是公平模式,有两种实现:NonfairSyncFairSync

  • NonfairSync:非公平模式。线程调用 acquire 时直接尝试获取许可,不保证顺序。
  • FairSync:公平模式。线程获取许可遵循队列顺序。
abstract static class Sync extends AbstractQueuedSynchronizer {
    Sync(int permits) {
        setState(permits);  // 设置初始许可数
    }

    final int getPermits() {
        return getState();
    }

    final int nonfairTryAcquireShared(int acquires) {
        for (;;) {
            int available = getState();
            int remaining = available - acquires;
            if (remaining < 0 || compareAndSetState(available, remaining))
                return remaining;
        }
    }

    protected final boolean tryReleaseShared(int releases) {
        for (;;) {
            int current = getState();
            int next = current + releases;
            if (compareAndSetState(current, next))
                return true;
        }
    }
}
  • 非公平模式(nonfairTryAcquireShared:直接尝试获取许可,如果成功则更新 state
  • 公平模式(FairSync:遵循 AQS 的队列顺序,确保 FIFO 访问。

三、Semaphore 的使用方法

Semaphore 提供了三种主要方法来操作许可:

  • acquire():获取一个许可,若没有许可则阻塞。
  • release():释放一个许可,唤醒等待的线程。
  • tryAcquire():尝试获取许可,但不阻塞。
Semaphore semaphore = new Semaphore(3); // 初始许可数为 3

// 获取许可,若无可用许可则阻塞
semaphore.acquire();      

// 释放许可
semaphore.release();

// 尝试获取许可,若不可用立即返回 false,不会阻塞
if (semaphore.tryAcquire()) {
    // do something
}

acquire() 源码

public void acquire() throws InterruptedException {
    sync.acquireSharedInterruptibly(1);
}

acquireSharedInterruptibly(1) 会调用 nonfairTryAcquireShared(1)tryAcquireShared(公平模式),尝试获取许可,若没有足够许可则阻塞等待。

release() 源码

public void release() {
    sync.releaseShared(1);
}

releaseShared(1) 增加许可数并唤醒阻塞线程,使等待线程得以继续执行。

四、Semaphore 的应用场景

Semaphore 非常适合控制对有限资源的访问,典型的应用场景有:

1. 连接池限流

在数据库连接池中,可以使用 Semaphore 限制同时访问连接的线程数。例如,数据库连接数有限,通过 Semaphore 控制同时访问的线程数量,避免过度负载。

public class DatabaseConnectionPool {
    private static final int MAX_CONNECTIONS = 5;
    private final Semaphore semaphore = new Semaphore(MAX_CONNECTIONS);

    public Connection getConnection() throws InterruptedException {
        semaphore.acquire();
        return acquireConnection();
    }

    public void releaseConnection(Connection conn) {
        releaseConnection(conn);
        semaphore.release();
    }
}

2. 控制线程并发数

在高并发任务中,Semaphore 可限制同时执行的线程数量,例如,控制下载任务的并发数,避免过多线程导致的系统负载过高。

3. 资源分配和限流

Semaphore 适合资源共享场景,如共享打印机、固定线程数的线程池等,确保资源不会被过度占用。

五、与其他 JUC 工具的对比

1. CountDownLatch

  • 许可机制:Semaphore 的许可数可以动态变化,而 CountDownLatch 的计数器只能递减。
  • 适用场景:Semaphore 控制并发资源访问数量,而 CountDownLatch 用于线程等待。

2. CyclicBarrier

  • 作用:Semaphore 适合控制资源访问并发数,而 CyclicBarrier 则用于线程间的同步点,使所有线程达到某个屏障时一起继续。
  • 灵活性:Semaphore 更灵活,可随时 release(),不必等待所有线程。

3. ReentrantLock

  • 模式:ReentrantLock 是排他锁,每次只允许一个线程访问。Semaphore 允许多个线程共享资源(多个许可)。
  • 适用场景:ReentrantLock 适合独占资源场景,Semaphore 适合资源池、限流等。

4. Phaser

  • 复杂度:Phaser 用于复杂的多阶段同步,可以动态增加/减少线程,而 Semaphore 主要用于固定数量资源控制。
  • 适用性:Phaser 适合分阶段任务同步,而 Semaphore 适合控制资源并发数。

庐山烟雨浙江潮,未至千般恨不消。

到得还来别无事,庐山烟雨浙江潮。

近半年不会再更新了,居家办公的日子要结束了。读书,世界就在眼前;不读书,眼前就是世界。与你共勉!

标签:JUC,许可,int,acquire,工具包,线程,Semaphore,release
From: https://www.cnblogs.com/lgx211/p/18538270

相关文章

  • 一文彻底弄懂JUC工具包的CountDownLatch的设计理念与底层原理
    CountDownLatch是Java并发包(java.util.concurrent)中的一个同步辅助类,它允许一个或多个线程等待一组操作完成。一、设计理念CountDownLatch是基于AQS(AbstractQueuedSynchronizer)实现的。其核心思想是维护一个倒计数,每次倒计数减少到零时,等待的线程才会继续执行。它的主要设......
  • JUC容器
    并发容器类这些类专为支持并发环境中的高效数据访问和操作而设计。与传统的容器类相比,并发容器类具有更好的线程安全性和性能。在使用多线程环境时,通常推荐使用这些并发容器以避免手动加锁和同步操作。ConcurrentHashMap特点:一个线程安全的哈希表,支持高效的并发访问。通过分......
  • 诛仙3:幻心千劫|单机安装教程|虚拟机一键端|GM工具包
    天给大家带来一款单机游戏的架设:诛仙3-幻心千劫-16职业。游戏版本:v4.4.0只适用于单机娱乐,此教程是本人亲测所写,踩坑无数,如果你是小白跟着教程走也是可以搭建  亲测视频演示https://githubs.xyz/show/297.mp4 游戏安装步骤此游戏架设需要安装虚拟机,没......
  • 诛仙3:梦起河阳|单机安装教程|虚拟机一键端|GM工具包
    天给大家带来一款单机游戏的架设:诛仙3-梦起河阳-16职业。游戏版本:v4.4.0只适用于单机娱乐,此教程是本人亲测所写,踩坑无数,如果你是小白跟着教程走也是可以搭建成功   亲测视频演示https://githubs.xyz/show/296.mp4 游戏安装步骤此游戏架设需要安装虚拟机,......
  • 三个常见JUC辅助类
    三个常见JUC辅助类1.减少计数(CountDownLatch)​通过一个计数器来管理需要等待的线程数量,当这些线程都完成各自的任务后(即计数器递减到0),才会允许其他等待的线程继续执行。步骤:定义CountDownLatch类,并设置一个固定值在需要计数的位置加上countDown()方法使用await()......
  • python-17-包和模块-创建属于自己的python工具包
    python-17-包和模块一.说明python中的基础系列关于组织代码的基本单位就是包和模块,在真实项目中我们不可能将所有代码都写在一起,或者我们的一些工具类库等需要单独处理,方便各模块调用,怎么办?这时候包和模块就来了,可以很方便的帮我们组织代码。来开始我们今天的日拱一卒!。......
  • 诛仙2:末日与曙光|单机安装教程|虚拟机一键端|GM工具包
    今天给大家带来一款单机游戏的架设:诛仙2:末日与曙光。12职业,游戏版本:v3.0.9只适用于单机娱乐,此教程是本人亲测所写,踩坑无数,如果你是小白跟着教程走也是可以搭建成功   亲测视频演示https://githubs.xyz/show/292.mp4 游戏安装步骤此游戏架设需......
  • 诛仙2:时光之书|单机安装教程|虚拟机一键端|GM工具包
    今天给大家带来一款单机游戏的架设:诛仙2:时光之书。11职业,游戏版本:v3.0.1只适用于单机娱乐,此教程是本人亲测所写,踩坑无数,如果你是小白跟着教程走也是可以搭建成功        亲测视频演示https://githubs.xyz/show/292.mp4 游戏安装步骤......
  • 诛仙2:荣耀之路|单机安装教程|虚拟机一键端|GM工具包
    今天给大家带来一款单机游戏的架设:诛仙2:荣耀之路。游戏版本:v2.2.9只适用于单机娱乐,此教程是本人亲测所写,踩坑无数,如果你是小白跟着教程走也是可以搭建成功   亲测视频演示https://githubs.xyz/show/292.mp4 游戏安装步骤此游戏架设需要安装虚拟机,......
  • 诛仙2:为爱成神|单机安装教程|虚拟机一键端|GM工具包
    今天给大家带来一款单机游戏的架设:诛仙2:为爱成神。游戏版本:v2.2.8只适用于单机娱乐,此教程是本人亲测所写,踩坑无数,如果你是小白跟着教程走也是可以搭建成功     亲测视频演示https://githubs.xyz/show/291.mp4 游戏安装步骤此游戏架设需要安装虚拟机......