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日

相关文章

  • 微信小程序自定义弹窗实现详解(可通用)

    微信小程序自定义弹窗实现详解(可通用) 1.需求分析 在小程序中,有时候需要弹出一个自定义样式的弹窗,以提示用户或获得用户的确认。 2.实现过程 2.1 自定义组件 首先,在小程序中我们需要创建一个自定义组件,用于承载弹窗的内容。在组件的 wxml 文件中,可以编写弹窗的样式和内容。 <!– 弹窗组件 –> <view class=&q…

    other 2023年6月25日
    00
  • vue3学习指导教程(附带获取屏幕可视区域宽高)

    Vue3 学习指导教程 简介 Vue3 是 Vue.js 的最新版本,相比于 Vue2,它具有更高的性能、更好的 TypeScript 支持和更加灵活的组合式 API 等诸多优势。本教程将手把手地教你如何学习 Vue3,以及如何在学习过程中获取屏幕可视区域宽高。 学习 Vue3 安装 要开始学习 Vue3,首先需要安装 Vue3。可以通过以下命令安装最新的 …

    other 2023年6月27日
    00
  • 什么是域和域控制器 Windows 2003域控制器设置/客户端安装及问题处理

    域和域控制器 简介 在计算机网络中,域是指一组计算机、用户和设备的集合,可以通过集中的管理方式来管理这些计算机、用户和设备。域控制器是用于管理域的服务器,它处理登录验证、资源访问控制、用户和计算机的管理等任务。 Windows 2003域控制器设置 系统要求 Windows Server 2003 操作系统 确保计算机符合硬件要求 如果需要远程管理域控制器,…

    other 2023年6月25日
    00
  • Spring整合Mybatis 扫描注解创建Bean报错的解决方案

    问题解析 在 Spring 整合 Mybatis 时,我们通常会使用注解的方式配置 Mybatis。在扫描 mapper 接口和 mapper.xml 文件时,我们需要在配置文件中添加以下两行配置: <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">…

    other 2023年6月26日
    00
  • 魔兽7.0惩戒骑属性优先级该怎么选择_惩戒骑属性收益优先级介绍

    魔兽7.0惩戒骑属性优先级选择攻略 1. 简介 在魔兽世界7.0版本中,惩戒骑士是一种强大的近战攻击职业,其属性的选择对于提升输出能力至关重要。本攻略将会介绍惩戒骑士的属性收益优先级,帮助玩家正确选择和优化属性分配。 2. 属性收益优先级介绍 2.1 主要属性 力量(Strength):主要影响惩戒骑士的攻击强度和物理伤害输出。 爆击(Critical St…

    other 2023年6月28日
    00
  • 浅谈Python 参数与变量

    浅谈Python 参数与变量 在Python中,参数和变量是编程中非常重要的概念。参数是函数定义时用于接收外部传入值的变量,而变量则是用于存储数据的容器。本文将详细讲解Python中参数和变量的使用方法。 参数 位置参数 位置参数是指在函数定义时按照顺序声明的参数,调用函数时需要按照相同的顺序传入对应的值。下面是一个示例: def add(x, y): re…

    other 2023年8月15日
    00
  • Python 使用元类type创建类对象常见应用详解

    以下是使用元类type创建类对象的常见应用的完整攻略: Python 使用元类type创建类对象常见应用 在Python中,可以使用元类type来动态创建类对象。元类是用于创建类的类,通过定义元类,我们可以在运行时动态地创建类对象。 示例1:动态创建类对象 MyClass = type(‘MyClass’, (), {‘x’: 1, ‘y’: 2}) obj…

    other 2023年10月14日
    00
  • Angular.js中控制器之间的传值详解

    Angular.js中控制器之间的传值详解 在Angular.js中,控制器之间的传值是非常常见和重要的操作。下面将详细讲解如何在Angular.js中实现控制器之间的传值,并提供两个示例说明。 1. 使用服务(Service)进行传值 Angular.js中的服务是一个可被多个控制器共享的对象。通过在服务中定义变量或方法,我们可以在不同的控制器之间传递数据…

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