首页 > 编程语言 >Java并发类的主要API方法-Semaphore

Java并发类的主要API方法-Semaphore

时间:2024-08-13 22:53:36浏览次数:16  
标签:Java 许可 int 获取 API 线程 Semaphore 资源

一、Semaphore

emaphore 是 Java 并发包 (java.util.concurrent) 中的一个同步工具类,类Semaphore所提供的功能完全就是synchronized关键字的升级版,但它提供的功能更加的强大与方便,主要的作用就是控制线程并发的数量,而这一点,单纯地使用synchronized是做不到的。

emaphore 它用来控制访问某个资源的线程数量。你可以将 Semaphore 想象成一个计数器,它控制着对某个共享资源的访问许可(permits)的数量。当许可用完时,其他线程必须等待,直到有线程释放许可。

emaphore 定义最多允许多少个线程执行acquire()和release()之间的代码。

为什么要限制线程的数量?

—— 如果不限制线程并发的数量,则CPU的资源很快就被耗尽,每个线程执行的任务是相当缓慢,因为CPU要把时间片分配给不同的线程对象,而且上下文切换也要耗时,最终造成系统运行效率大幅降低,所以限制并发线程的数量还是非常有必要的。

Semaphore 的基本用法

• Semaphore(int permits): 创建一个带有给定许可数量的 Semaphore。

• acquire(): 获取一个许可。如果没有许可可用,则阻塞,直到有许可可用。

• release(): 释放一个许可,将其返回给 Semaphore。

1.1 Semaphore 示例:停车场模拟

假设有一个停车场,最多只能容纳 3 辆车。Semaphore 可以用来控制停车场的进入和离开。

 

import java.util.concurrent.Semaphore;

public class ParkingLot {
    private final Semaphore semaphore;

    public ParkingLot(int slots) {
        // 创建一个 Semaphore,允许的最大并发数是 slots
        this.semaphore = new Semaphore(slots);
    }

    public void parkCar(String car) {
        try {
            System.out.println(car + " is trying to enter the parking lot.");
            // 获取一个许可,可能会阻塞直到有可用的许可
            semaphore.acquire();
            System.out.println(car + " has parked.");
            // 模拟停车一段时间
            Thread.sleep((long) (Math.random() * 10000));
            System.out.println(car + " is leaving the parking lot.");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 释放许可,表示车已经离开
            semaphore.release();
            System.out.println(car + " has left the parking lot.");
        }
    }

    public static void main(String[] args) {
        ParkingLot parkingLot = new ParkingLot(3); // 停车场有3个车位

        Runnable car = () -> {
            String carName = Thread.currentThread().getName();
            parkingLot.parkCar(carName);
        };

        // 创建多个线程来模拟不同的车
        for (int i = 1; i <= 5; i++) {
            new Thread(car, "Car " + i).start();
        }
    }
}

运行结果示例:

Car 1 is trying to enter the parking lot.
Car 1 has parked.
Car 2 is trying to enter the parking lot.
Car 2 has parked.
Car 3 is trying to enter the parking lot.
Car 3 has parked.
Car 4 is trying to enter the parking lot.
Car 5 is trying to enter the parking lot.
Car 1 is leaving the parking lot.
Car 1 has left the parking lot.
Car 4 has parked.
...

说明

Semaphore(3):停车场允许 3 辆车同时进入。

acquire():车想要进入停车场。如果没有空位(许可用完),它将等待。

release():车离开停车场,释放一个许可。

当所有 3 个许可都被占用时,其他车必须等待,直到有车离开并释放许可为止。这个示例展示了如何使用 Semaphore 控制对共享资源(停车位)的并发访问。

1.2 常用方法

以下是 Semaphore 常用的方法及其功能说明:

1. 构造方法

• Semaphore(int permits): 创建一个许可数为 permits 的 Semaphore,许可数是可以被线程获取的信号量的数量。

• Semaphore(int permits, boolean fair): 创建一个许可数为 permits 的 Semaphore,并指定是否采用公平策略。如果 fair 为 true,则线程将按先来先得的顺序获取许可;如果为 false,则线程的顺序是未指定的(更高效)。

2. 获取许可

• void acquire() throws InterruptedException: 从信号量中获取一个许可。如果没有可用的许可,则线程会被阻塞,直到有许可被释放为止。

• void acquire(int permits) throws InterruptedException: 从信号量中获取指定数量的许可,如果没有足够的许可,线程会被阻塞。

• boolean tryAcquire(): 尝试获取一个许可,如果成功则返回 true,否则返回 false。此方法不会阻塞线程。

• boolean tryAcquire(int permits): 尝试获取指定数量的许可,如果成功则返回 true,否则返回 false。此方法不会阻塞线程。

• boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException: 在给定的超时时间内尝试获取一个许可,如果在超时时间内获取到了许可则返回 true,否则返回 false。

• boolean tryAcquire(int permits, long timeout, TimeUnit unit) throws InterruptedException: 在给定的超时时间内尝试获取指定数量的许可。

3. 释放许可

• void release(): 释放一个许可,将其返回到信号量中。

• void release(int permits): 释放指定数量的许可,将其返回到信号量中。

4. 查询许可

• int availablePermits(): 返回当前可用的许可数。

• int drainPermits(): 获取并返回当前所有可用的许可,并将其从信号量中移除(即将可用许可数设置为0)。

• boolean hasQueuedThreads(): 检查是否有线程正在等待获取许可。

• int getQueueLength(): 返回正在等待获取许可的线程数。

1.3 Semaphore+ReentrantLock+Condition实例

package tools;

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

public class ListPool {

    private int poolMaxSize = 3;
    private int semaphorePermits = 5;
    private List<String> list = new ArrayList<String>();
    private Semaphore concurrencySemaphore = new Semaphore(semaphorePermits);
    private ReentrantLock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    public ListPool() {
        super();
        for (int i = 0; i < poolMaxSize; i++) {
            list.add("高洪岩" + (i + 1));
        }
    }

    public String get() {
        String getString = null;
        try {
            concurrencySemaphore.acquire();
            lock.lock();
            while (list.size() == 0) {
                condition.await();
            }
            getString = list.remove(0);
            lock.unlock();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return getString;
    }

    public void put(String stringValue) {
        lock.lock();
        list.add(stringValue);
        condition.signalAll();
        lock.unlock();
        concurrencySemaphore.release();
    }

}
线程类MyThread.java代码如下:
package extthread;

import tools.ListPool;

public class MyThread extends Thread {

    private ListPool listPool;

    public MyThread(ListPool listPool) {
        super();
        this.listPool = listPool;
    }

    @Override
    public void run() {
        for (int i = 0; i < Integer.MAX_VALUE; i++) {
            String getString = listPool.get();
            System.out.println(Thread.currentThread().getName() + " 取得值 "
                    + getString);
            listPool.put(getString);
        }
    }

}
运行类Run.java代码如下:
package test;

import tools.ListPool;
import extthread.MyThread;

public class Run {

    public static void main(String[] args) {

        ListPool pool = new ListPool();

        MyThread[] threadArray = new MyThread[12];
        for (int i = 0; i < threadArray.length; i++) {
            threadArray[i] = new MyThread(pool);
        }
        for (int i = 0; i < threadArray.length; i++) {
            threadArray[i].start();
        }

    }
}

这个代码实现了一个简单的资源池管理系统,其中资源池 (ListPool) 中保存了一些可共享的资源(字符串),并通过并发控制工具 Semaphore 和 ReentrantLock 来控制多个线程对资源的并发访问。以下是对代码的分析和功能说明:

1. ListPool 类

ListPool 类是一个资源池管理类,维护了一个包含字符串资源的列表 list,并通过以下几种并发控制机制来管理资源的获取和释放:

Semaphore concurrencySemaphore:用于限制并发访问的线程数量。该信号量的许可数设置为 5,表示最多允许 5 个线程同时访问资源池。

ReentrantLock lockCondition condition:用于实现对共享资源列表的同步访问。当资源池为空时,线程会在 condition 上等待,直到有其他线程向资源池中添加资源。

主要方法:

get(): 从资源池中获取一个资源。

• 通过 concurrencySemaphore.acquire() 获取一个许可,控制并发访问。

• 使用 lock.lock() 获取锁,以确保对 list 的访问是线程安全的。

• 如果资源池为空(list.size() == 0),线程会等待 condition.await(),直到有资源可用。

• 从资源池中移除并返回一个资源。

• 释放锁以允许其他线程访问资源池。

put(String stringValue): 将一个资源放回资源池。

• 使用 lock.lock() 获取锁,以确保对 list 的访问是线程安全的。

• 将资源添加到资源池中,并通过 condition.signalAll() 通知所有等待的线程资源可用。

• 释放锁。

• 通过 concurrencySemaphore.release() 释放许可,以便其他等待的线程可以继续获取资源。

2. MyThread 类

MyThread 是一个线程类,每个线程会不断地从资源池中获取资源,然后再将资源放回池中。它的 run() 方法中包含了一个无限循环,执行如下步骤:

• 调用 listPool.get() 从资源池中获取一个资源。

• 打印当前线程获取到的资源。

• 调用 listPool.put() 将资源放回资源池。

3. Run 类

Run 类是程序的入口点,主要做以下事情:

• 创建一个 ListPool 对象,初始化资源池。

• 创建并启动 12 个 MyThread 线程,每个线程都共享同一个 ListPool 实例。

代码的实现功能

1. 资源池管理: 资源池 ListPool 维护了一个包含 3 个字符串资源的列表。通过 get() 方法,线程可以从资源池中获取资源,通过 put() 方法将资源放回池中。

2. 并发控制: 使用 Semaphore 限制了同时访问资源池的线程数最多为 5;使用 ReentrantLock 和 Condition 确保在资源不足时,线程会等待并在资源可用时被唤醒。

3. 资源复用: 线程通过不断获取和释放资源,实现了资源的循环利用。

运行效果

当代码运行时,12 个线程会并发地尝试从资源池中获取和释放资源。由于 Semaphore 的许可数是 5,所以最多有 5 个线程能够同时获取资源,其他线程则会等待,直到有线程释放资源后才能继续获取资源。通过这种机制,实现了对有限资源的安全、高效的并发访问和复用。

标签:Java,许可,int,获取,API,线程,Semaphore,资源
From: https://blog.csdn.net/sinat_33536503/article/details/141143544

相关文章

  • Java数组06:常见排序算法
    1.冒泡排序冒泡排序(BubbleSort),是一种计算机科学领域的较简单的排序算法。它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果他们的顺序(如从大到小、首字母从A到Z)错误就把他们交换过来。走访元素的工作是重复地进行直到没有相邻元素需要交换,也就是说该元素列已经排序完......
  • Java数组07:稀疏数组
    1.线性结构线性结构是最常用的数据结构,其特点是数据元素之间存在一对一的线性关系。线性结构有两种不同存储结构,即顺序存储结构和链式存储结构。顺序存储的线性表称为顺序表,顺序表中的存储元素是连续的,即在内存中是连续的,例如数组。链式存储的线性表称为链表,链表中的存储元......
  • Java数组05:Arrays 类
    数组的工具类java.util.Arrays由于数组对象本身并没有什么方法可以供我们调用,但API中提供了一个工具类Arrays供我们使用,从而可以对数据对象进行一些基本的操作。文档简介:此类包含用来操作数组(比如排序和搜索)的各种方法。此类还包含一个允许将数组作为列表来查看的静态工厂。......
  • 一款Java 性能监控和统计工具——MyPerf4J!【送源码】
    背景随着所在公司的发展,应用服务的规模不断扩大,原有的垂直应用架构已无法满足产品的发展,几十个工程师在一个项目里并行开发不同的功能,开发效率不断降低。于是公司开始全面推进服务化进程,把团队内的大部分工程师主要精力全部都集中到服务化中。服务化可以让每个工程师仅在自己......
  • Java基础-学习笔记10
    **10内部类**内部类一个类的内部又完整的嵌套了另一个类的结构,被嵌套的类成为内部类,嵌套其他类的类称为外部类。是类的第五大成员(属性、方法、构造器、代码块、内部类)。内部类最大的特点就是可以直接访问私有属性,并且可以体现类与类之间的包含关系。classOuter//外部类{......
  • Java数组04:多维数组
    多维数组可以看成是数组的数组,比如二维数组就是一个特殊的一维数组,其每一个元素都是一个一维数组。多维数组的动态初始化(以二维数组为例)直接为每一维分配空间,格式如下:type[][]typeName=newtype[typeLength1][typeLength2];type可以为基本数据类型和复合数据类型,arraylen......
  • java
    markdown二级标题字体markdownmarkdownmarkdownmarkdown引用哈哈哈分割线图片![截图](C:\Users\黄刘洋\Pictures\Screenshots\屏幕截图2024-08-13161113.png)超链接点击跳转4399列表AA表格名字性别生日张三男1977.1.1代码publi......
  • Java数组03:数组使用
    数组的元素类型和数组的大小都是确定的,所以当处理数组元素时候,我们通常使用基本循环或者ForEach循环。【该实例完整地展示了如何创建、初始化和操纵数组】publicclassTestArray{ publicstaticvoidmain(String[]args){ double[]myList={1.9,2.9,3.4,3.5}; /......
  • Java数组02:数组声明创建
    1.声明数组首先必须声明数组变量,才能在程序中使用数组。下面是声明数组变量的语法:dataType[]arrayRefVar;//首选的方法或dataTypearrayRefVar[];//效果相同,但不是首选方法建议使用dataType[]arrayRefVar的声明风格声明数组变量。dataTypearrayRefVar[]风格是来......
  • JDBC在java代码中的使用
    声明对于数据库的增删改查,在使用jdbc的过程中分二类,查(DQL)语句和增,删,改(DML语句)他们的整体都分为以下五部分,只是DQL语句多了数据的处理部分。在使用之前需要导入相关的jar包 1,加载驱动器(非必要,但是建议手动加载养成好习惯)2,创建连接3,创建会话4,执行会话5,关闭会话1,数据......