当前位置: 首页 > article >正文

Semaphore 与 线程池 Executor 有什么区别?

前言:笔者在看Semaphone时 突然脑子宕机,啥啥分不清 Semaphore 和 Executor 的作用,故再次记录。

一、什么是Semaphore?

SemaphoreJava 并发编程(JUC)中一个重要的同步工具,它的作用控制对特定资源的并发访问

通过 Semaphore,你可以限制 同时访问某些资源 线程数量。它主要用于解决 多线程访问共享资源时 可能出现的资源竞争问题。

二、Semaphore 的基本原理

Semaphore 内部维护了一个计数器,表示 允许访问资源 线程数量。线程通过调用 acquire() 方法来获取一个许可(减少计数器),通过调用 release() 方法来释放一个许可(增加计数器)。当计数器为 0 时,acquire() 方法会让线程阻塞,直到有其他线程释放许可,从而使计数器增加。

三、基本操作

  • acquire():尝试获取一个许可。如果没有可用的许可,线程会被阻塞,直到有其他线程释放许可。
  • release():释放一个许可,增加可用许可数量,如果有线程在等待许可,释放许可后这些线程会被唤醒。

四、构造方法

Semaphore 提供了两种构造方法:

  1. Semaphore(int permits):初始化一个具有  指定许可数量  的信号量。
  2. Semaphore(int permits, boolean fair):指定是否公平获取许可。fair 参数表示是否启用公平锁机制,确保线程按请求顺序获取许可;如果不公平(false),则获取许可的顺序是随机的。

五、基本示例

import java.util.concurrent.Semaphore;

public class SemaphoreExample {
    private static final Semaphore semaphore = new Semaphore(3);  // 初始化为 3,表示最多允许 3 个线程同时访问

    public static void main(String[] args) {
        // 启动 5 个线程,模拟资源竞争
        for (int i = 0; i < 5; i++) {
            new Thread(new Task()).start();
        }
    }

    static class Task implements Runnable {
        @Override
        public void run() {
            try {
                System.out.println(Thread.currentThread().getName() + " 尝试获得许可.");
                semaphore.acquire();  // 请求一个许可
                System.out.println(Thread.currentThread().getName() + " 获得了一个许可.");

                // 模拟任务执行
                Thread.sleep(1000);

                System.out.println(Thread.currentThread().getName() + " 释放一个许可.");
                semaphore.release();  // 释放许可
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }
}

1、解释

  • 在上面的代码中,Semaphore 的许可数量为 3,意味着最多只有 3 个线程可以同时访问资源(即,执行任务)。
  • 当一个线程调用 acquire() 时,如果有足够的许可,它就能执行;如果没有许可,线程会被阻塞。
  • 当线程完成任务后,调用 release() 释放许可,其他等待的线程就可以继续执行。

2、一次运行的示例

Thread-0 尝试获得许可.
Thread-2 尝试获得许可.
Thread-1 尝试获得许可.
Thread-1 获得了一个许可.
Thread-2 获得了一个许可.
Thread-4 尝试获得许可.
Thread-3 尝试获得许可.
Thread-0 获得了一个许可.
Thread-2 释放一个许可.
Thread-0 释放一个许可.
Thread-1 释放一个许可.
Thread-3 获得了一个许可.
Thread-4 获得了一个许可.
Thread-3 释放一个许可.
Thread-4 释放一个许可.

六、应用场景

Semaphore 通常用于以下场景:

  1. 限制并发数:控制对某些资源的访问数。例如,数据库连接池或线程池中并发访问资源的数量。
  2. 限制并发线程数:在某些系统中,我们希望限制同时运行的线程数量,防止资源过度消耗或系统崩溃。例如,限制 Web 服务器同时处理的请求数。
  3. 多线程协调Semaphore 也可以用来实现多线程之间的协调,例如保证某些线程的顺序执行或同步。

七、与线程池(Executor)的区别

使用线程池(Executor最大线程数确实可以限制并发任务的数量,从而在某种程度上控制任务访问的并发度。但 Semaphore 和线程池 通过不同的方式控制并发,关键的区别在于控制对象控制的粒度

1. 控制粒度

  • 线程池(Executor):线程池通过控制线程数量来限制并发。你可以设置线程池的核心线程数最大线程数,通过最大线程数来限制同时执行的任务数量

    • 线程池更多的是管理任务的执行,而不是直接控制 任务访问共享资源的次数。线程池会分配线程来执行任务,但每个任务本身不涉及对资源的直接管理,线程池的控制只是确保不会过多线程同时运行
    • 例如,假设线程池大小是 N,你最多可以同时运行 N 个任务。这限制了并发任务的数量,但并不限制每个任务对资源的访问次数。
  • SemaphoreSemaphore 控制的是资源访问的并发性。假设你有一个有限的共享资源(如数据库连接、网络连接、文件、IO操作等),你可以通过 Semaphore 限制同时能够访问该资源的线程数量。即使你有一个线程池,线程池中的线程数也不一定能反映出每个任务对资源的实际并发访问次数Semaphore 是对资源访问的细粒度控制。

    • 例如,假设你有一个数据库连接池,最大并发连接数为 5,那么即使线程池有 10 个线程,只有 5 个线程能够同时访问数据库连接。其他线程会被阻塞,直到有线程释放连接。

2. 总结

  • 线程池 限制的是任务的并发数,控制的是线程的数量
  • Semaphore 限制的是对资源的并发访问,控制的是访问某个共享资源的线程数

http://www.kler.cn/a/522779.html

相关文章:

  • 【QT】- QUdpSocket
  • 【数据结构】_顺序表经典算法OJ(力扣版)
  • JavaScript中的隐式类型转换
  • kafka-部署安装
  • NoteGen:记录、写作与AI融合的跨端笔记应用
  • 1.23 补题 寒假训练营
  • 嵌入式知识点总结 Linux驱动 (三)-文件系统
  • Linux 35.6 + JetPack v5.1.4之编译器升级
  • 在Rust应用中访问.ini格式的配置文件
  • vim如何解决‘’文件非法关闭后,遗留交换文件‘’的问题
  • 第3章 基于三电平空间矢量的中点电位平衡策略
  • 无人机+固定机巢 ,空地协同作业技术详解
  • Hive:Hive Shell技巧
  • 回顾:Maven的环境搭建
  • 第32章 测试驱动开发(TDD)的原理、实践、关联与争议(Python 版)
  • 【设计模式-行为型】迭代器模式
  • 构建自定义 AI 模型服务:集成到 Spring AI 处理特定任务
  • 算法刷题Day28:BM66 最长公共子串
  • AAAI2024论文合集解读|Multi-granularity Causal Structure Learning-water-merged
  • 82,【6】BUUCTF WEB .[CISCN2019 华东南赛区]Double Secret
  • 电脑怎么格式化?格式化详细步骤
  • App UI自动化--Appium学习--第一篇
  • 【机器学习】自定义数据集 使用tensorflow框架实现逻辑回归并保存模型,然后保存模型后再加载模型进行预测
  • LabVIEW相位差测量系统
  • [微服务]服务保护原理
  • Redis数据库笔记——数据类型及其底层实现