JAVA 多线程之信号量(Semaphore)实例详解

JAVA 多线程之信号量(Semaphore)实例详解

什么是信号量

信号量是操作系统中的一种同步工具,它可以用来控制同时访问某些资源的线程数量。Semaphore 是 Java 开发中一个用于控制线程的工具类,它可以用于控制同时执行的线程数量,可以看作是一种限流的方式。

Semaphore 所提供的计数器是被初始化的,并且该计数器有一个上限,它表示的是共享资源的个数。当某个线程需要访问共享资源时,它必须在这个 Semaphore 中占用一个许可(permit)。当许可被占用时,Semaphore 的计数器会减去 1,当计数器为 0 时,将不再允许执行占用许可的线程,其它的线程必须等待已下占用的线程释放许可。当占用许可的线程使用完共享资源后,它需要释放许可,这样 Semaphore 的计数器会加 1,表示又有一个许可可以被占用。

Semaphore 的使用

Semaphore 的常用方法主要有两个:acquire()release()

acquire() 方法用于申请一个许可,如果当前 Semaphore 的计数器大于 0,就可以申请许可,计数器值减 1。如果当前 Semaphore 的计数器为 0,调用 acquire() 方法的线程将一直阻塞,直到有许可可用为止。

release() 方法用于释放一个许可,计数器增加 1。

在使用 Semaphore 时,需要注意的是:申请和释放许可的线程必须是同一个线程。

示例一

下面是一个简单的示例,演示 Semaphore 的使用:

import java.util.concurrent.Semaphore;

public class SemaphoreDemo {
    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(2);  // 初始化信号量为 2
        new MyThread(semaphore, "A").start();
        new MyThread(semaphore, "B").start();
        new MyThread(semaphore, "C").start();
        new MyThread(semaphore, "D").start();
    }

    static class MyThread extends Thread {
        private Semaphore semaphore;

        public MyThread(Semaphore semaphore, String name) {
            super(name);
            this.semaphore = semaphore;
        }

        @Override
        public void run() {
            try {
                semaphore.acquire();   // 获取许可
                System.out.println(Thread.currentThread().getName() + " 获取许可...");
                Thread.sleep(2000);  // 模拟线程执行任务需要占用许可
                semaphore.release();   // 释放许可
                System.out.println(Thread.currentThread().getName() + " 释放许可...");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

在上面的示例中,我们创建了一个 Semaphore 对象,并将其初始值设置为 2。然后我们创建了 4 个线程,并将 Semaphore 对象传递给这些线程。在线程中,我们先尝试获取许可,如果获取到许可,则打印获取许可的信息,并模拟线程执行任务需要占用许可的时间;随后调用 release() 方法释放许可,并打印释放许可的信息。

运行该程序,输出如下:

A 获取许可...
C 获取许可...
C 释放许可...
D 获取许可...
A 释放许可...
D 释放许可...
B 获取许可...
B 释放许可...

从输出可以看出,同一时刻只有两个线程可以获取到许可,并且每个线程获得许可之后都需要释放许可,才能让其它线程获取许可。

示例二

下面是另外一个示例,演示使用 Semaphore 模拟某个共享资源的访问:

import java.util.concurrent.Semaphore;

public class SemaphoreDemo {
    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(3);  // 初始化信号量为 3
        new ShareResource(semaphore, "A").start();
        new ShareResource(semaphore, "B").start();
        new ShareResource(semaphore, "C").start();
    }

    static class ShareResource extends Thread {
        private Semaphore semaphore;

        public ShareResource(Semaphore semaphore, String name) {
            super(name);
            this.semaphore = semaphore;
        }

        @Override
        public void run() {
            try {
                System.out.println(Thread.currentThread().getName() + " 尝试访问共享资源...");
                semaphore.acquire();   // 获取许可
                System.out.println(Thread.currentThread().getName() + " 成功访问共享资源");
                Thread.sleep(2000);  // 模拟线程执行任务需要占用许可
                semaphore.release();   // 释放许可
                System.out.println(Thread.currentThread().getName() + " 释放了许可");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

在上面的示例中,我们创建了一个 Semaphore 对象,并将其初始值设置为 3。然后我们创建了 3 个线程,并将 Semaphore 对象传递给这些线程。在线程中,我们尝试获取许可,如果获取到许可,则打印成功访问共享资源的信息,并模拟线程执行任务需要占用许可的时间;随后调用 release() 方法释放许可,并打印释放许可的信息。

运行该程序,输出如下:

A 尝试访问共享资源...
A 成功访问共享资源
B 尝试访问共享资源...
B 成功访问共享资源
C 尝试访问共享资源...
C 成功访问共享资源
A 释放了许可
B 释放了许可
C 释放了许可

从输出可以看出,在同一时刻只有 3 个线程可以成功访问共享资源。每个线程访问共享资源之前都需要获取许可,访问结束后需要释放许可。如果没有许可可用,则线程会一直阻塞,直到有许可可用为止。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:JAVA 多线程之信号量(Semaphore)实例详解 - Python技术站

(0)
上一篇 2023年6月27日
下一篇 2023年6月27日

相关文章

  • Java高级语法学习之反射详解

    Java高级语法学习之反射详解 什么是反射 Java中的反射机制是指在程序运行时,动态获取类的信息并操作它们的一种机制。通过反射,程序可以获取某个类的构造方法、成员变量、成员方法,并可以在运行时动态创建对象、调用方法、访问变量等。 反射的应用场景 动态加载类: 在程序运行时,根据外部条件动态加载指定的类; 动态创建对象: 可以根据传入的类名和参数,动态创建该…

    other 2023年6月27日
    00
  • Java中字符串常见题之String相关讲解

    Java中字符串常见题之String相关讲解 String类的定义 在Java中,String是一个类,它代表字符串类型。 String类是final类,它是Java的内置类之一,也是Java程序中最常用的类之一。 String的常用方法 创建字符串对象 直接赋值 java String str1 = “Hello World”; 构造函数 java Str…

    other 2023年6月20日
    00
  • VS2019开发简单的C/C++动态链接库并进行调用的实现

    下面我将详细讲解如何使用VS2019开发简单的C/C++动态链接库并进行调用的完整攻略,包含以下步骤: 步骤一:创建动态链接库项目 打开Visual Studio 2019,选择 创建新项目。 在 新建项目 弹出框中,选择 Windows桌面向导 面板,选择 动态链接库 (.dll) 项目类型。 为项目命名并选择保存位置,点击 创建。 步骤二:编写动态链接库…

    other 2023年6月26日
    00
  • 详解使用React进行组件库开发

    详解使用React进行组件库开发攻略 本攻略将详细介绍如何使用React进行组件库开发。我们将涵盖从设置项目到构建和发布组件库的整个过程。 步骤1:设置项目 首先,我们需要设置一个新的React项目来开始组件库的开发。按照以下步骤进行操作: 使用create-react-app命令行工具创建一个新的React项目: npx create-react-app …

    other 2023年7月27日
    00
  • JavaScript 继承详解(五)

    JavaScript 继承是面向对象编程中常见的概念,本篇文章主要介绍了以下五种继承方式:原型继承、借用构造函数继承、组合继承、寄生组合式继承、class继承。 原型继承 原型继承是指通过 prototype 对象的原型链进行继承。子类的 prototype 原型链指向了父类的实例,从而实现继承。示例如下: function Parent() { this.…

    other 2023年6月27日
    00
  • 无畏契约弹VAN9003错误怎么办 瓦罗兰特VAN9003错误解决方法

    无畏契约弹VAN9003错误怎么办? 如果你在玩《无畏契约》游戏时,遇到 VAN9003 错误并不是什么罕见的问题。这个错误通常会显示为“无法连接到服务器”的提示,并且会阻止你进入游戏。这个错误的原因可能有很多,可能是由于网络问题,也可能是游戏客户端或服务器问题导致的。接下来,我们将为大家介绍一些可能的解决方法。 解决方法一:检查网络连接 由于 VAN900…

    other 2023年6月27日
    00
  • 关于mysql:经度和纬度数据类型和存储格式

    关于MySQL:经度和纬度数据类型和存储格式 在MySQL中,可以使用DECIMAL数据类型来存储经度和纬度数据。以下是关于MySQL经度和纬度数据类型和存储格式的完整攻略: 经度和纬度数据类型 经度和纬度数据类型都使用DECIMAL数据类型来存储。DECIMAL数据类型用于存储精确的小数值,可以指定精度和小数位数。在存储经度和纬度,通常将精度设置为10,小…

    other 2023年5月8日
    00
  • C语言示例讲解结构体的声明与初始化方法

    下面是“C语言示例讲解结构体的声明与初始化方法”的完整攻略: 1. 结构体的声明方法 结构体是C语言中一种自定义的数据类型,它可以同时存储多个不同类型的数据,通过结构体可以将多个变量打包成一个整体,方便操作和管理。 结构体的声明方法如下: struct [结构体名]{ [成员1类型] 成员1; [成员2类型] 成员2; … [成员n类型] 成员n; };…

    other 2023年6月20日
    00
合作推广
合作推广
分享本页
返回顶部