Java中的Semaphore如何使用

使用 Semaphore 可以控制同时访问资源的线程个数,在 Java 中,Semaphore 是一个计数信号量。

Semaphore 可以用来限制某个资源的访问线程个数,它的构造函数接收一个整型变量 n,表示同一时刻最多允许 n 个线程访问该资源。当一个线程进入该资源进行访问时,计数器会减去 1,其他线程再访问时就会被阻塞,直到该线程释放资源时计数器加 1。Semaphore 可以控制资源的访问数量,是一个非常重要的并发控制工具。

Semaphore 类的基本用法

Semaphore 类是 Java.util.concurrent 包下的类,使用它需要导入以下包:

import java.util.concurrent.Semaphore;

Semaphore 类的构造器、方法参数都是 int 型的,说明 Semaphore 可以协调的线程数量必须是整数类型,解决某一个范围的访问问题。

Semaphore 类有以下方法:

  • acquire(): 获取一个许可证,如果没有可用的许可证,则会阻塞直到有可用的许可证;
  • release(): 释放一个许可证,释放一个阻塞的线程;
  • tryAcquire(): 尝试获取一个许可证,并立即返回 true 或 false;
  • tryAcquire(long timeout, TimeUnit unit): 在指定的时间内尝试获取一个许可证;
  • availablePermits(): 获取当前可用的许可证数量。

简单示例:

下面的代码简单演示了如何使用 Semaphore,假设有 10 个资源,现在有 30 个线程需要访问这 10 个资源,每次只能有 3 个线程同时访问,其他线程需要等待当前 3 个线程访问完后才能访问。

import java.util.concurrent.Semaphore;

public class SemaphoreDemo {
    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(3); // 初始化信号量,最多允许 3 个线程访问
        for (int i = 0; i < 30; i++) {
            Thread t = new Thread(() -> {
                try {
                    semaphore.acquire(); // 获取一个许可证
                    System.out.println(Thread.currentThread().getName() + "开始访问资源");
                    Thread.sleep(2000); // 模拟访问资源需要 2s
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    semaphore.release(); // 释放一个许可证
                    System.out.println(Thread.currentThread().getName() + "结束访问资源");
                }
            }, "Thread-" + i);
            t.start();
        }
    }
}

该示例会启动 30 个线程,每个线程尝试去获取一个许可证,当许可证数量不足时,线程会被阻塞,直到获取到许可证后才能继续执行,然后线程释放许可证并结束访问。

Semaphore 与 Condition 一起使用

Semaphore 与 Condition 可以协同工作,Semaphore 负责控制线程的并发访问数量,Condition 负责控制线程的等待和通知,这样可以实现更复杂的并发控制。下面的示例演示了 Semaphore 和 Condition 一起使用的方法。

假设现在有两个线程,线程 1 执行任务 A,线程 2 执行任务 B,任务 B 需要依赖任务 A 的结果,在任务 A 执行完后通知任务 B 开始执行。

import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class SemaphoreWithConditionDemo {
    private static final Semaphore semaphore1 = new Semaphore(1);
    private static final Semaphore semaphore2 = new Semaphore(0);

    public static void main(String[] args) {
        ReentrantLock lock = new ReentrantLock();
        Condition condition = lock.newCondition();
        Thread t1 = new Thread(() -> {
            lock.lock();
            try {
                System.out.println("Thread-1执行任务A");
                Thread.sleep(2000);
                condition.signal(); // 通知线程 2 执行任务B
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }, "Thread-1");

        Thread t2 = new Thread(() -> {
            semaphore1.acquire(); // 获取 semaphore 1 的许可证
            lock.lock();
            try {
                System.out.println("Thread-2等待Thread-1执行完任务A,开始执行任务B");
                Thread.sleep(2000);
                semaphore2.release(); // 释放 semaphore 2 的许可证
                System.out.println("Thread-2执行完任务B");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }, "Thread-2");

        t2.start();
        t1.start(); // 等待线程 1 完成任务 A,通知线程 2 执行任务 B
        lock.lock();
        try {
            condition.await(); // 等待线程 1 完成任务 A
            System.out.println("Thread-1执行完任务A,通知Thread-2执行任务B");
            semaphore1.release(); // 释放 semaphore 1 的许可证
            semaphore2.acquire(); // 获取 semaphore 2 的许可证
            System.out.println("Thread-1继续执行任务");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

该示例会启动两个线程,线程 1 执行任务 A,线程 2 等待线程 1 执行完任务 A 后执行任务 B,任务 B 需要依赖任务 A 的结果,当任务 A 完成后通知线程 2 开始执行任务 B。实现该示例需要使用 Semaphore 和 Condition 类,Semaphore 的作用是限制同时执行任务的线程数量,Condition 用于通知线程 2 开始执行任务 B。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java中的Semaphore如何使用 - Python技术站

(0)
上一篇 2023年5月26日
下一篇 2023年5月26日

相关文章

  • Java字符串常见的操作(比较,查找,替换等)

    Java字符串常见的操作 在Java中,字符串被定义为一个类,称为java.lang.String。Java中的字符串是不可变的,这意味着一旦创建了一个字符串,就不能更改它的内容,而是会创建一个新的字符串对象。 下面是Java字符串常见的操作: 创建字符串 在Java 中创建一个字符串非常容易,只需要将字符串括在两个引号之间即可: String str = …

    Java 2023年5月26日
    00
  • SpringBoot中获取微信用户信息的方法

    获取微信用户信息的方法,一般分为两个步骤: 获取用户的授权凭证(code) 根据授权凭证(code)换取用户的openid和access_token SpringBoot已经整合了微信的SDK,可直接使用。 步骤一:获取用户的授权凭证(code) 用户在访问我们的网站或应用时,需要先登录微信,然后授权给我们的应用。这时我们就可以得到用户的code。 用如下代…

    Java 2023年5月26日
    00
  • 一文带你搞懂Java8的LocalDateTime

    一文带你搞懂Java8的LocalDateTime 什么是LocalDateTime LocalDateTime是Java 8提供的一个时间类型,表示本地日期和时间,不包含时区信息。它是LocalDate和LocalTime的结合体,提供了更加方便的操作方式和更加清晰的概念。 获取LocalDateTime实例 使用LocalDateTime.now()方法…

    Java 2023年5月20日
    00
  • Java使用lambda自定义Arrays.sort排序规则说明

    前言 Java是一门面向对象的编程语言,对象与对象之间的交互及其相关的逻辑一直都是Java编程中的一个重点。 Java中的集合类是十分重要的,它们包含了大量的数据结构及算法,帮助Java开发者在日常开发工作中处理各种数据结构问题,其中最常用的是数组。 Java的Arrays类提供了sort方法,使我们可以对数组进行排序,不过Arrays.sort方法提供的排…

    Java 2023年5月26日
    00
  • SpringBoot中的配置类(@Configuration)

    SpringBoot中的 @Configuration 类是一个特殊的类,其作用是为Spring容器提供Bean定义,用来替代传统的XML配置文件。这样,我们就可以通过在Java中编写@Configuration类,来使得Spring容器中的Bean定义更加方便、直观。下面我将详细讲解SpringBoot中的配置类(@Configuration)。 1. @…

    Java 2023年5月15日
    00
  • 什么是同步代码块?

    以下是关于同步代码块的完整使用攻略: 同步代码块 同步代码块是指在多线程编程中,使用 synchronized 关键字来实现对共享资源的访问控制的一种方式。同步代码块可以将需要同步的代码块包裹起来,从而保证同一时间只有一个线程可以访问共享资源,避免线程之间的竞争和冲突。 同步代码块的语法格式如下: synchronized (object) { // 需要同…

    Java 2023年5月12日
    00
  • Spring Security自定义认证逻辑实例详解

    来详细讲解一下“Spring Security自定义认证逻辑实例详解”的完整攻略。 1. 概述 Spring Security是一个功能强大的安全框架,提供了包括认证、授权、攻击防范等在内的综合安全解决方案。在Spring Security中,认证是一个非常重要的环节。本攻略旨在详细讲解Spring Security中如何自定义认证逻辑。 2. 前置条件 在…

    Java 2023年5月20日
    00
  • Java 超详细讲解ThreadLocal类的使用

    Java 超详细讲解ThreadLocal类的使用 什么是ThreadLocal? ThreadLocal是Java的一个线程封闭工具类,它允许当前线程存储和获取某个值,并且这个值对其他线程是不可见的。 通常情况下,我们需要共享数据之间遵循建议: 不可变的元素在任何情况下都是线程安全的。 可变的元素在并发情况下,应该遵循不同的访问方式,例如同步访问。 不可变…

    Java 2023年5月19日
    00
合作推广
合作推广
分享本页
返回顶部