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();
                }
            }
    
        }
    }
    
    
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>