暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Semaphore:共享资源的夜店入场券——线程们的857蹦迪现场!

📢 【夜店风云】Semaphore:线程们的857蹦迪现场!保安:里面人满了,排队去!


🌃 夜店名场面:线程の狂欢

深夜12点,**"Semaphore夜店"**门口大排长龙!

  • 线程A(穿着new关键字潮牌):“我有VIP,让我先进!”

  • 线程B(顶着黑眼圈):“我刚release()出来,怎么又要排队?!”

  • 线程C(996刚下班):“我就想acquire()个卡座,这么难吗?”

保安(Semaphore)冷着脸:“里面就3个舞池位置,出来一个才能进一个!”

(画外音:当你的代码试图同时操作100个线程访问数据库连接池时,数据库:“我裂开了!”)


🎫 Semaphore是什么?

它就像夜店保安手里的入场券

  1. 许可证总量:夜店容量(比如3个卡座)

  2. acquire():抢到券才能进(没券?门口等着!)

  3. release():嗨完离场,退还入场券

代码版夜店蹦迪

    Semaphore semaphore = new Semaphore(3); // 只有3个舞池位置
    IntStream.range(010).forEach(i -> new Thread(() -> {
        try {
            semaphore.acquire(); // 求求了,给张票吧
            System.out.println("线程"+i+"进入舞池!开始857!");
            Thread.sleep(2000); // 疯狂摇摆
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            semaphore.release(); // 跳累了,溜了溜了
            System.out.println("线程"+i+"离开舞池!");
        }
    }).start());

    输出:

      线程0进入舞池!开始857!  
      线程1进入舞池!开始857!  
      线程2进入舞池!开始857!  
      2秒后)  
      线程0离开舞池!  
      线程3进入舞池!开始857!  
      ...  

      🔍 原理解密:AQS的限量人生

      Semaphore的核心是许可证计数器

      1. state变量:记录剩余入场券数量

      2. 非公平模式(默认):新线程可以插队抢票(VIP特权!)

      3. 公平模式:严格按先来后到排队(保安:都给我老实等着!)

      4. CLH队列:没抢到票的线程在虚拟队列里蹲着

      (画外音:当你在代码里用new Semaphore(3, true)
      时,保安秒变正义使者:“排队!必须排队!”)


      👨💻 手搓"夜店保安系统"(乞丐版)

      用synchronized+wait/notify实现最简Semaphore:

        public class MySemaphore {
            private int permits;
            
            public MySemaphore(int permits) {
                this.permits = permits;    
            }


                public synchronized void acquire() throws InterruptedException {
                while (permits <= 0) {
                    wait(); // 保安:没票了,一边等着!
                }
                permits--; // 成功抢到票
            }
            public synchronized void release() {
                permits++; // 退还入场券
                notifyAll(); // 保安喊:有人出来了!
            }
        }

        测试代码

          MySemaphore semaphore = new MySemaphore(2); // 卫生间只有2个坑位
          new Thread(() -> {
              try {
                  semaphore.acquire();
                  System.out.println("线程A占领坑位...");
                  Thread.sleep(3000); // 带薪拉屎
              } catch (InterruptedException e) {
                  Thread.currentThread().interrupt();
              } finally {
                  semaphore.release();
              }
          }).start();
          // 其他线程同理...

          💣 夜店潜规则(避坑指南)

          1. release()不能不调:离场不还券?夜店迟早倒闭(内存泄漏警告!)

          2. 信号量别乱增release()
            超过初始值?保安:“我凭空变出个卡座?”

          3. tryAcquire()灵活用if(tryAcquire()) { 蹦迪 } else { 回家 }
             —— 拒绝死等!

          4. 小心信号量劫持:比如用semaphore.acquire(permits)
            一次性抢光所有票(包场玩法!)


          🎮 实战:数据库连接池

            public class ConnectionPool {
                private final Semaphore semaphore;
                private final Deque<Connection> pool = new ArrayDeque<>();
                public ConnectionPool(int size) {
                    this.semaphore = new Semaphore(size);
                    IntStream.range(0, size).forEach(i -> 
                        pool.add(createConnection()); // 创建连接
                }
                public Connection getConn() throws InterruptedException {
                    semaphore.acquire(); // 等入场券
                    synchronized (pool) {
                        return pool.removeFirst();
                    }
                }
                public void releaseConn(Connection conn) {
                    synchronized (pool) {
                        pool.addLast(conn);
                    }
                    semaphore.release(); // 还券
                }
            }

            (画外音:当你的同事忘记release()时,整个系统逐渐变成——“连接池已空,等不到的永远在骚动~”)


            👉 关注【让天下没有难学的编程】
            下期预告:《ThreadLocal:线程的私房钱保险柜——谁也别想动我的变量!》

            彩蛋
            当主线程试图用Semaphore控制子线程时:

              Semaphore semaphore = new Semaphore(0);  
              new Thread(() -> {  
                  doSomething();  
                  semaphore.release(); // 工具人实锤  
              }).start();  
              semaphore.acquire(); // 主线程:“你不干完活别想走!” 

              (保安:这届线程太难带了,还得当监工!)


              文章转载自让天下没有难学的编程,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

              评论