并发编程系列之Semaphore

本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

原文链接:blog.ouyangsihai.cn >> 并发编程系列之Semaphore

并发编程系列之Semaphore

前言

上节我们介绍了Java中的并发工具类CountDownLatch和Cyclicbarrier,今天我们再来说说另外两个并发工具类:Semaphore(信号量)和Exchanger(交换者),首先我们先来说说信号量这个东西,结合我们今天所讲的,我们了解下,这个东西到底有啥用,话不多说,让我们开启今天的并发之旅吧。

并发编程系列之Semaphore

什么是信号量?

信号量是用来控制同时访问特定资源的线程数量,通过协调各个线程,以保证合理的使用公共资源。

这样说可能大家不是很理解,那我就举个生活中常见的场景,结合场景描述下Semaphore这个东西:

我们大家都去过大型商场,都知道商场都有自己的停车场,假设一个停车场允许的最大停车数量是200,当我们到达停车场时,会发现上面显示着剩余车位,如果剩余车位0,那么栏杆会扫描完车牌放行,如果剩余车位=0,则说明已没有能使用的车位,此时就不放行,所以进入商场的车都需要排队等候,当里面有离开停车场,离开n辆就会放行n辆,这里用来控制是否放行的剩余车位数就可以理解为是一个信号量指示,成功扫描,或者拿到停车票的车子就能进入,他用来控制当前进入商场的车是否能放行。

并发编程系列之Semaphore

如何使用信号量?

使用信号量我们首先要创建一个信号量


public Semaphore(int permits) {
        sync = new NonfairSync(permits);
    }

然后就可以调用Semaphore的相关方法,主要有如下几个方法:


// 获取一个信号量许可证 
public void acquire() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
}

// 释放获取的信号量许可证,一次释放一个
public void release() {
        sync.releaseShared(1);
}

// 返回信号量中可以使用的许可证数
public int availablePermits() {
        return sync.getPermits();
}

// 返回正在等待获取许可证的线程数
public final int getQueueLength() {
    return sync.getQueueLength();
}

// 返回Boolean值是否存在线程正在等待获取许可证
public final boolean hasQueuedThreads() {
   return sync.hasQueuedThreads();
}

// 返回所有正在等待获取许可证的线程集合 AQS操作
protected CollectionThread getQueuedThreads() {
        return sync.getQueuedThreads();
}

// AQS中的源码
 public final CollectionThread getQueuedThreads() {
        ArrayListThread list = new ArrayListThread();
        for (Node p = tail; p != null; p = p.prev) {
            Thread t = p.thread;
            if (t != null)
                list.add(t);
        }
        return list;
    }

OK,下面我们看看如何使用信号量,我们使用Semaphore来模拟一个停车场停车的场景:


public class SemaphoreDemo {
    public static void main(String[] args) {
        // 线程池
        ExecutorService exec = Executors.newCachedThreadPool();
        // 假设停车场一共有停车位5个
        final Semaphore semp = new Semaphore(5);
        // 模拟10个车子进入停车场
        for (int index = 10000; index  10011; index++) {
            final int NO = index;
            Runnable run = new Runnable() {
                public void run() {
                    try {
                        // 获取许可
                        semp.acquire();
                        System.out.println("获得停车证进场车牌号:沪A" + NO);
                        // 模拟一个车子在停车场停留时间
                        Thread.sleep((long) (Math.random() * 10000));
                        // 访问完后,释放,离开停车场
                        semp.release();
                        System.out.println("沪A" + NO+"离开停车场");
                    } catch (InterruptedException e) {
                    }
                }
            };
            exec.execute(run);
        }
        try {
            Thread.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 退出线程池
        exec.shutdown();
    }
}

执行结果:

并发编程系列之Semaphore 并发编程系列之Semaphore

其实Semaphore也可以理解为是实现了等待唤醒功能,你也可以使用wait/notifyAll来实现上述模拟,感兴趣的小伙伴可以自己试试。

并发编程系列之Semaphore

原文始发于微信公众号(Justin的后端书架):

本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

原文链接:blog.ouyangsihai.cn >> 并发编程系列之Semaphore


 上一篇
并发编程系列之线程池工厂类——Executors 并发编程系列之线程池工厂类——Executors
前言 上节讲了讲自定义线程池,今天我们来聊聊线程池框架,在实际开发中我们还是基本使用线程框架Executor给我们提供的一些工具类,Java提供的Executor都在JUC(java.util.concurrent)包下面,主要包括:
2021-04-05
下一篇 
并发编程系列之自定义线程池 并发编程系列之自定义线程池
前言 前面我们在讲并发工具类的时候,多次提到线程池,今天我们就来走进线程池的旅地,首先我们先不讲线程池框架Executors,我们今天先来介绍如何自己定义一个线程池,是不是已经迫不及待了,那么就让我们开启今天的旅途吧。 什么是线
2021-04-05