多线程之--- 信号量Semaphore

写这篇博客的目的是增加自己记忆,方便平常使用

1.信号量

Semaphore 是一种基于计数的信号量,它可以是一个阀值,基于此,多个线程竞争获取许可信号,超过阀值后,线程申请许可信号将会别阻塞

常用方法

方法 含义
acquire() 申请一个信号量获取可用的资源,如果可用, 信号量内部的资源个数减掉1,如果没有可用资源线程会阻塞在该方法中,不能结束该方法,不能返回,直到有可用的资源为止
release() 释放资源,释放后信号量内部的资源个数会增加1,果有被阻塞的线程,释放后会唤醒一个线程去获取资源,acquire() 和 release()要成对使用,一般release()放在finally代码块中
availablePermits() 当前可用的资源个数, permits - availablePermits() = 正在使用的资源个数

结合CountDownLatch 闭锁 使用方式如下:

@Slf4j
public class CountExample {
    //同时并发执行的总数
    private static int threadTotal = 200;
    //请求总数
    private static int clientTotal = 5000;
    //验证实际请求的总数   会自增1
    private static int count = 0;

    public static void main(String[] args) throws Exception{
        //创建一个可缓存的线程池,如果线程长度超过处理需要,可灵活回收线程,若无可回收,则新建线程
        ExecutorService executor = Executors.newCachedThreadPool();
       final Semaphore semaphore = new Semaphore(threadTotal);
       final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
        for (int i = 0; i < clientTotal; i++) {
            executor.execute(() -> {
                try {
                    //得到一个信号量
                    semaphore.acquire();
                    add();
                    //释放信号量
                    semaphore.release();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    log.error("exception",e);
                }
                // 减 1
                countDownLatch.countDown();
            });
        }
        //阻塞,对计数没任何影响
        countDownLatch.await();
        //关闭线程池
        executor.shutdown();
        log.info("完成count:{}", count);
    }

    private static void add(){
        count++;
    }

}

返回结果

[main] INFO com.mmall.concurrency.mufeng.basic.CountExample - 完成count:4970

期望值是5000,实际值为 4970,由此可见这个执行方法是线程不安全的。

下面这个是线程安全写法:采用AtomicInteger

@Slf4j
@ThreadSafe
public class CountExample2 {

    // 请求总数
    public static int clientTotal = 5000;

    // 同时并发执行的线程数
    public static int threadTotal = 200;

    public static AtomicInteger count = new AtomicInteger(0);

    public static void main(String[] args) throws Exception {
        ExecutorService executorService = Executors.newCachedThreadPool();
        final Semaphore semaphore = new Semaphore(threadTotal);
        final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
        for (int i = 0; i < clientTotal ; i++) {
            executorService.execute(() -> {
                try {
                    semaphore.acquire();
                    add();
                    semaphore.release();
                } catch (Exception e) {
                    log.error("exception", e);
                }
                countDownLatch.countDown();
            });
        }
        countDownLatch.await();
        executorService.shutdown();
        log.info("count:{}", count.get());
    }

    private static void add() {
        count.incrementAndGet();
        // count.getAndIncrement();
    }
}

执行结果:

[main] INFO com.mmall.concurrency.example.count.CountExample2 - count:5000

因为采用线程安全类 AtomicInteger 所以在自增1运算时,可以保证线程安全

你可能感兴趣的:(多线程)