Java 多线程之 Semaphore(信号量/限流/同步辅助类)
一、概述
-
Semaphore(信号量)是一种并发控制机制,用于控制对共享资源的访问。它维护了一个计数器,可以限制同时访问某个资源的线程数量。常用于限制同时访问某个资源的线程数量,例如控制数据库连接池的并发访问、控制线程池的并发任务数、生产者-消费者问题、读者-写者问题等。
-
使用 Semaphore ,就像银行取钱一样。假如银行有3个柜台,那么同一时刻最多就只能有3个人取钱,其他人需在等待区等待。当其中某个柜台的人走了以后,等待区的人才能有一个人到柜台办理取钱业务。从而实现限流,保证了柜台业务的正常运作,否则都挤过去就乱了。
-
Semaphore 有两个主要操作:acquire() 和 release()。
- acquire(): 当一个线程需要访问共享资源时,它可以调用 acquire() 方法。如果计数器大于0,那么线程将获得访问权限,并将计数器减1。如果计数器为0,那么线程将被阻塞,直到有其他线程释放资源并增加计数器。
- release(): 当一个线程完成对共享资源的访问时,它应该调用 release() 方法来释放资源并增加计数器的值。这将允许其他线程获取访问权限。
- tryAcquire():尝试获取一个许可证,如果获取成功返回 true,否则返回 false,不会阻塞线程。
-
注意:Semaphore 通常用来限流,即限制访问某个资源的线程数。而不是用于斥锁资源的访问,如果要用于斥锁资源的问题,则需把初始计数器设置为1。这时它变成了一个二元信号量,通常被称为互斥锁。只允许一个线程同时访问共享资源。
二、使用方法
-
使用 Semaphore 的方法如下:
- 声明一个 Semaphore 对象,初始化时指定 同时访问资源 的线程个数。
- 在业务开始部分使用 semaphore.acquire(); 领取许可证。
- 完成业务后使用 semaphore.release(); 还回许可证。
public class SemaphoreExample { private Semaphore semaphore = new Semaphore(5); // 允许同时访问资源的线程数量,这里设置为5,表示可以有5个线程同时访问 public void accessResource() { try { semaphore.acquire(); // 获取许可证,如果有许可证,则计数器减1;如果没有可用许可证,则阻塞 // 访问共享资源的代码 } catch (InterruptedException e) { // 处理中断异常 } finally { semaphore.release(); // 释放资源,计数器加1 } } }
三、测试示例
-
在下面示例中,创建了一个 BankSemaphoreExample 类,模拟银行有5个柜台可以进行取钱业务。在主线程中创建100个线程,模拟100个富豪,然后一起去银行取钱。由于银行使用 Semaphore 限流,同一时刻只能有5个人取钱,其他人会排队等候。
package top.yiqifu.study.p004_thread; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Semaphore; public class Test086_Semaphore { public static void main(String[] args) { BankSemaphoreExample bank = new BankSemaphoreExample(); List<Thread> threads = new ArrayList(); for(int i = 1; i<= 100; i++){ Thread t = new Thread(()->{ try { bank.getMoney(Thread.currentThread().getName()); Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } }); t.setName("富豪"+i); threads.add(t); } for(Thread t : threads){ t.start(); } for(Thread t : threads){ try { t.join(); } catch (InterruptedException e) { e.printStackTrace(); } } } public static class BankSemaphoreExample { private volatile float Money = 1000 * 10000; private Semaphore semaphore = new Semaphore(5); // 假高银行有5个柜台 synchronized void get(float m) { this.Money -= m; } public void getMoney(String people){ try { semaphore.acquire(); float m = (float) Math.random()* 10000; this.get(m); System.out.println(people+ " 取钱"+m+"元,还有排队富豪="+semaphore.getQueueLength()); Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } finally { semaphore.release(); } } } }