多线程之Semaphore

123 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第26天,点击查看活动详情

1 说明

Semaphore 信号量,用来限制能同时访问共享资源的线程上限.

public static void main(String[] args) {
     // 1. 创建 semaphore 对象
     Semaphore semaphore = new Semaphore(3);
    
     // 2. 10个线程同时运行
     for (int i = 0; i < 10; i++) {
         new Thread(() -> {
             // 3. 获取许可
             try {
                 semaphore.acquire();
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }
             try {
                 log.debug("running...");
                 sleep(1);
                 log.debug("end...");
             } finally {
                 // 4. 释放许可
                 semaphore.release();
             }
         }).start();
     }
 }

运行结果:

07:35:15.485 c.TestSemaphore [Thread-2] - running... 
07:35:15.485 c.TestSemaphore [Thread-1] - running... 
07:35:15.485 c.TestSemaphore [Thread-0] - running... 
07:35:16.490 c.TestSemaphore [Thread-2] - end... 
07:35:16.490 c.TestSemaphore [Thread-0] - end... 
07:35:16.490 c.TestSemaphore [Thread-1] - end... 
07:35:16.490 c.TestSemaphore [Thread-3] - running... 
07:35:16.490 c.TestSemaphore [Thread-5] - running... 
07:35:16.490 c.TestSemaphore [Thread-4] - running... 
07:35:17.490 c.TestSemaphore [Thread-5] - end... 
07:35:17.490 c.TestSemaphore [Thread-4] - end... 
07:35:17.490 c.TestSemaphore [Thread-3] - end... 
07:35:17.490 c.TestSemaphore [Thread-6] - running... 
07:35:17.490 c.TestSemaphore [Thread-7] - running... 
07:35:17.490 c.TestSemaphore [Thread-9] - running... 
07:35:18.491 c.TestSemaphore [Thread-6] - end... 
07:35:18.491 c.TestSemaphore [Thread-7] - end... 
07:35:18.491 c.TestSemaphore [Thread-9] - end... 
07:35:18.491 c.TestSemaphore [Thread-8] - running... 
07:35:19.492 c.TestSemaphore [Thread-8] - end... 

2 应用

  • Semaphore 限流,在访问高峰期时,让请求线程阻塞,高峰期过去再释放许可,当然它只适合限制单机 线程数量,并且仅是限制线程数,而不是限制资源数.

  • Semaphore 实现简单连接池,对比『享元模式』下的实现(用wait notify),性能和可读性显然更好.

class Pool {
     // 1. 连接池大小
     private final int poolSize;
     // 2. 连接对象数组
     private Connection[] connections;
     // 3. 连接状态数组 0 表示空闲, 1 表示繁忙
     private AtomicIntegerArray states;
     private Semaphore semaphore;
    
     // 4. 构造方法初始化
     public Pool(int poolSize) {
         this.poolSize = poolSize;
         // 让许可数与资源数一致
         this.semaphore = new Semaphore(poolSize);
         this.connections = new Connection[poolSize];
         this.states = new AtomicIntegerArray(new int[poolSize]);
         for (int i = 0; i < poolSize; i++) {
             connections[i] = new MockConnection("连接" + (i+1));
         }
     }
    
     // 5. 借连接
     public Connection borrow() {// t1, t2, t3
         // 获取许可
         try {
             semaphore.acquire(); // 没有许可的线程,在此等待
         } catch (InterruptedException e) {
             e.printStackTrace();
         }

         for (int i = 0; i < poolSize; i++) {
             // 获取空闲连接
             if(states.get(i) == 0) {
                 if (states.compareAndSet(i, 0, 1)) {
                     log.debug("borrow {}", connections[i]);
                     return connections[i];
                 }
             }
         }

         // 不会执行到这里
         return null;
     }
     // 6. 归还连接
     public void free(Connection conn) {
         for (int i = 0; i < poolSize; i++) {
             if (connections[i] == conn) {
                 states.set(i, 0);
                 log.debug("free {}", conn);
                 semaphore.release();
                 break;
             }
         }
     }
}