Java并发类的主要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 个线程能够同时获取资源,其他线程则会等待,直到有线程释放资源后才能继续获取资源。通过这种机制,实现了对有限资源的安全、高效的并发访问和复用。

  • 13
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值