Java 并发编程 - Semaphore

1、关于 Semaphore

 
       Semaphore 是一个计数信号量,必须由获取它的线程释放。Semaphore 是 synchronized 的加强版,作用是控制线程的并发数量。就这一点而言,单纯的synchronized 关键字是实现不了的。常用于限制可以访问某些资源的线程数量,例如通过 Semaphore 限流。
 
Semaphore 只有3个操作:

  • 初始化
  • 增加(获取)
  • 减少(释放)
     

2、acquire 和 release 方法

2.1 acquire 和 release 有参方法

  • acquire(int permits) :表示每调用1次此方法,就使用Semaphore中的x(方法中的参数)个permits。
  • release(int permits) :表示每调用1次此方法,就释放Semaphore中的x(方法中的参数)个permits。
     
package com.lcao.aqs;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

/**
 * @author
 * @title: SemaphoreExample
 * @description: Semaphore 信号量 例子
 * @date 2020/4/20 11:03
 */
@Slf4j
public class SemaphoreExample {
    
    


    private static Integer threadCount = 20;

    public static void main(String[] args) {
    
    

        ExecutorService threadPool = Executors.newCachedThreadPool();
        // 实例化时定义信号量许可数,这里允许20
        final Semaphore semaphore = new Semaphore(20);

        for (int i=0; i<threadCount;i++) {
    
    
            final int num = i;

            threadPool.execute(()->{
    
    
                try {
    
    
                    // 获取5个许可,5表示进入此代码,就会消耗5个通路(许可),5个通路(许可)从20个中扣除
                    semaphore.acquire(5);  
                    testMethod(num);
                    semaphore.release(5);  // 释放占用的 5 个通路(许可)
                } catch (Exception e) {
    
    
                    log.info("多线程调用失败!");
                }

            });
        }
        threadPool.shutdown();
    }


    private static void testMethod(int num) throws Exception{
    
    
        Thread.sleep(1000);  // 这里是为了让控制台打印更直观
        log.info("{}-num = {}",Thread.currentThread().getId(),num);
    }

}

运行结果:


09:29:53.736 [pool-1-thread-4] INFO com.lcao.aqs.SemaphoreExample - 14-num = 3
09:29:53.736 [pool-1-thread-1] INFO com.lcao.aqs.SemaphoreExample - 11-num = 0
09:29:53.736 [pool-1-thread-3] INFO com.lcao.aqs.SemaphoreExample - 13-num = 2
09:29:53.736 [pool-1-thread-2] INFO com.lcao.aqs.SemaphoreExample - 12-num = 1
09:29:54.743 [pool-1-thread-6] INFO com.lcao.aqs.SemaphoreExample - 16-num = 5
09:29:54.743 [pool-1-thread-5] INFO com.lcao.aqs.SemaphoreExample - 15-num = 4
09:29:54.743 [pool-1-thread-7] INFO com.lcao.aqs.SemaphoreExample - 17-num = 6
09:29:54.743 [pool-1-thread-8] INFO com.lcao.aqs.SemaphoreExample - 18-num = 7
09:29:55.743 [pool-1-thread-9] INFO com.lcao.aqs.SemaphoreExample - 19-num = 8
09:29:55.743 [pool-1-thread-11] INFO com.lcao.aqs.SemaphoreExample - 21-num = 10
09:29:55.743 [pool-1-thread-10] INFO com.lcao.aqs.SemaphoreExample - 20-num = 9
09:29:55.743 [pool-1-thread-12] INFO com.lcao.aqs.SemaphoreExample - 22-num = 11
09:29:56.744 [pool-1-thread-14] INFO com.lcao.aqs.SemaphoreExample - 24-num = 13
09:29:56.744 [pool-1-thread-15] INFO com.lcao.aqs.SemaphoreExample - 25-num = 14
09:29:56.744 [pool-1-thread-13] INFO com.lcao.aqs.SemaphoreExample - 23-num = 12
09:29:56.744 [pool-1-thread-16] INFO com.lcao.aqs.SemaphoreExample - 26-num = 15
09:29:57.744 [pool-1-thread-20] INFO com.lcao.aqs.SemaphoreExample - 30-num = 19
09:29:57.744 [pool-1-thread-18] INFO com.lcao.aqs.SemaphoreExample - 28-num = 17
09:29:57.744 [pool-1-thread-19] INFO com.lcao.aqs.SemaphoreExample - 29-num = 18
09:29:57.744 [pool-1-thread-17] INFO com.lcao.aqs.SemaphoreExample - 27-num = 16

Process finished with exit code 0

 
分析:
       semaphore.acquire(5); 表示进入这行代码,就有5个通道(许可)被消耗。上述代码声明Semaphore时一共有20个通道(许可)。因为上述代码testMethod(int num)方法做了 Thread.sleep(1000)操作,所以控制台,每秒打印 20/5=4条日志记录。

2.2 acquire 和 release 无参方法

 
      查看 Semaphore 的部分源码,分析 acquire() 和 release() 有参方法与无参方法的区别。
 


package java.util.concurrent;
import java.util.Collection;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;


public class Semaphore implements java.io.Serializable {
    
    
    private static final long serialVersionUID = -3222578661600680210L;
    /** All mechanics via AbstractQueuedSynchronizer subclass */
    private final Sync sync;

    
    // 省略代码 ……



    /**
     * Creates a {@code Semaphore} with the given number of
     * permits and nonfair fairness setting.
     *
     * @param permits the initial number of permits available.
     *        This value may be negative, in which case releases
     *        must occur before any acquires will be granted.
     */
    public Semaphore(int permits) {
    
    
        sync = new NonfairSync(permits);
    }

   

    // 这里是 acquire() 无参方法
    public void acquire() throws InterruptedException {
    
    
        sync.acquireSharedInterruptibly(1);
    }
    
    // 这里是 acquire() 有参方法
    public void acquire(int permits) throws InterruptedException {
    
    
        if (permits < 0) throw new IllegalArgumentException();
        sync.acquireSharedInterruptibly(permits);
    }
    
    // 这里是 release() 无参方法
    public void release() {
    
    
        sync.releaseShared(1);
    }

    // 这里是 release() 有参方法
    public void release(int permits) {
    
    
        if (permits < 0) throw new IllegalArgumentException();
        sync.releaseShared(permits);
    }

    public boolean tryAcquire() {
    
    
        return sync.nonfairTryAcquireShared(1) >= 0;
    }

    
    public boolean tryAcquire(long timeout, TimeUnit unit)
        throws InterruptedException {
    
    
        return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
    }

    
    public boolean tryAcquire(int permits) {
    
    
        if (permits < 0) throw new IllegalArgumentException();
        return sync.nonfairTryAcquireShared(permits) >= 0;
    }

   
    public boolean tryAcquire(int permits, long timeout, TimeUnit unit)
        throws InterruptedException {
    
    
        if (permits < 0) throw new IllegalArgumentException();
        return sync.tryAcquireSharedNanos(permits, unit.toNanos(timeout));
    }


    // 省略代码 ……
   
    
}

       很明显 acquire() 和 release() 的无参方法,都是给了一个默认的许可数 1,等同于其各自的有参方法,只是有参方法的参数都是1.
 
示例:

package com.lcao.aqs;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

/**
 * @author 
 * @title: SemaphoreExample
 * @description: Semaphore 信号量 例子
 * @date 2020/4/20 11:03
 */
@Slf4j
public class SemaphoreExample {
    
    


    private static Integer threadCount = 20;

    public static void main(String[] args) {
    
    

        ExecutorService threadPool = Executors.newCachedThreadPool();
        // 实例化时定义信号量许可数,这里允许3
        final Semaphore semaphore = new Semaphore(3);

        for (int i=0; i<threadCount;i++) {
    
    
            final int num = i;

            threadPool.execute(()->{
    
    
                try {
    
    
                    semaphore.acquire();  // 默认获取1个许可
                    testMethod(num);
                    semaphore.release();  // 默认释放1个许可
                } catch (Exception e) {
    
    
                    log.info("多线程调用失败!");
                }

            });
        }
        threadPool.shutdown();
    }


    private static void testMethod(int num) throws Exception{
    
    
        Thread.sleep(1000);
        log.info("{}-num = {}",Thread.currentThread().getId(),num);
    }

}

 
运行结果:

11:51:14.883 [pool-1-thread-3] INFO com.lcao.aqs.SemaphoreExample - 13-num = 2
11:51:14.883 [pool-1-thread-2] INFO com.lcao.aqs.SemaphoreExample - 12-num = 1
11:51:14.883 [pool-1-thread-1] INFO com.lcao.aqs.SemaphoreExample - 11-num = 0
11:51:15.888 [pool-1-thread-5] INFO com.lcao.aqs.SemaphoreExample - 15-num = 4
11:51:15.888 [pool-1-thread-4] INFO com.lcao.aqs.SemaphoreExample - 14-num = 3
11:51:15.888 [pool-1-thread-6] INFO com.lcao.aqs.SemaphoreExample - 16-num = 5
11:51:16.889 [pool-1-thread-7] INFO com.lcao.aqs.SemaphoreExample - 17-num = 6
11:51:16.889 [pool-1-thread-8] INFO com.lcao.aqs.SemaphoreExample - 18-num = 7
11:51:16.889 [pool-1-thread-9] INFO com.lcao.aqs.SemaphoreExample - 19-num = 8
11:51:17.889 [pool-1-thread-11] INFO com.lcao.aqs.SemaphoreExample - 21-num = 10
11:51:17.889 [pool-1-thread-10] INFO com.lcao.aqs.SemaphoreExample - 20-num = 9
11:51:17.890 [pool-1-thread-12] INFO com.lcao.aqs.SemaphoreExample - 22-num = 11
11:51:18.890 [pool-1-thread-13] INFO com.lcao.aqs.SemaphoreExample - 23-num = 12
11:51:18.890 [pool-1-thread-14] INFO com.lcao.aqs.SemaphoreExample - 24-num = 13
11:51:18.891 [pool-1-thread-15] INFO com.lcao.aqs.SemaphoreExample - 25-num = 14
11:51:19.890 [pool-1-thread-16] INFO com.lcao.aqs.SemaphoreExample - 26-num = 15
11:51:19.890 [pool-1-thread-17] INFO com.lcao.aqs.SemaphoreExample - 27-num = 16
11:51:19.891 [pool-1-thread-18] INFO com.lcao.aqs.SemaphoreExample - 28-num = 17
11:51:20.891 [pool-1-thread-19] INFO com.lcao.aqs.SemaphoreExample - 29-num = 18
11:51:20.891 [pool-1-thread-20] INFO com.lcao.aqs.SemaphoreExample - 30-num = 19

Process finished with exit code 0

 
       这里由于 实例化时定义信号量许可数,这里允许3,代码:final Semaphore semaphore = new Semaphore(3),所以每秒只有3条日志信息打印。

3、tryAcquire 方法

  • tryAcquire() : 尝试获取一个许可,成功返回TRUE失败返回false 。
     
  • tryAcquire(int permits): 尝试获取 permits 个许可,成功返回TRUE失败返回false 。
     
  • tryAcquire(long timeout, TimeUnit unit):在一定时间内尝试获取一个许可。第一个参数是等待的时间,第二个是等待时间的单位。
     
  • tryAcquire(int permits, long timeout, TimeUnit unit):在一定时间内尝试获取 permits 个许可。第一个参数是等待的时间,第二个是等待时间的单位。
     

示例:

package com.lcao.aqs;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

/**
 * @author
 * @title: SemaphoreExample
 * @description: Semaphore 信号量 例子
 * @date 2020/4/20 11:03
 */
@Slf4j
public class SemaphoreExample {
    
    


    private static Integer threadCount = 20;

    public static void main(String[] args) {
    
    

        ExecutorService threadPool = Executors.newCachedThreadPool();
        // 实例化时定义信号量许可数,这里允许3
        final Semaphore semaphore = new Semaphore(6);

        for (int i=0; i<threadCount;i++) {
    
    
            final int num = i;

            threadPool.execute(()->{
    
    
                try {
    
    
                   if(semaphore.tryAcquire(2)) {
    
     // 尝试获取2个许可
                       testMethod(num);
                       semaphore.release(2);  // 释放2个许可
                   }
                } catch (Exception e) {
    
    
                    log.info("多线程调用失败!");
                }

            });
        }
        threadPool.shutdown();
    }


    private static void testMethod(int num) throws Exception{
    
    
        Thread.sleep(1000);
        log.info("{}-num = {}",Thread.currentThread().getId(),num);
    }

}

 
执行结果:


14:08:07.959 [pool-1-thread-2] INFO com.lcao.aqs.SemaphoreExample - 12-num = 1
14:08:07.959 [pool-1-thread-1] INFO com.lcao.aqs.SemaphoreExample - 11-num = 0
14:08:07.959 [pool-1-thread-3] INFO com.lcao.aqs.SemaphoreExample - 13-num = 2

Process finished with exit code 0

 
 
 
 
 
 
 
 
 
 
.

猜你喜欢

转载自blog.csdn.net/weixin_41922349/article/details/105652347
今日推荐