Semaphore线程信号量

文章目录

  • 前言
  • 一、Semaphore 是什么?
    • 定义
    • 对比
  • 二、使用步骤
    • 1. 场景分析
    • 2. 编码如下
  • 总结


前言

Semaphore 也是juc中的一个关键类,他与之前的lock 类似,也有公平和非公平两种,它与他们应用含义,引用场景有很大的不同; 与阻塞队列类似,但是也不一样;听我细细道来~


一、Semaphore 是什么?

定义

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

它用来控制访问资源的最大线程数量;
例如上厕所,只有十个坑位,所以最多只能允许十个人同时使用;

对比

  1. 对比lock 接口, lock只允许同一时间一个线程访问,而 Semaphore 同一时间允许多个线程访问
  2. 对比阻塞队列,应用场景类似,但也不一样; 阻塞队列 都是调节多线程协作,一块资源内容,允许多个线程同时访问, 也就是生产消费模式; 而 Semaphore 的使用场景也类似是这种生产消费的模式,单不同的是, 它是控制一个资源的多个线程访问数量;

Semaphore 的场景: 比如××马路要限制流量,只允许同时有一百辆车在这条路上行使,其他的都必须在路口等待,所以前一百辆车会看到绿灯,可以开进这条马路,后面的车会看到红灯,不能驶入××马路,但是如果前一百辆中有5辆车已经离开了××马路,那么后面就允许有5辆车驶入马路,这个例子里说的车就是线程,驶入马路就表示线程在执行,离开马路就表示线程执行完成,看见红灯就表示线程被阻塞,不能执行。

二、使用步骤

1. 场景分析

假如有一个需求,要读取几万个文件的数据,因为都是IO密集型任务,我们可以启动几十个线程并发地读取,但是如果读到内存后,还需要存储到数据库中,而数据库的连接数只有10个,这时我们必须控制只有10个线程同时获取数据库连接保存数据,否则会报错无法获取数据库连接,因为数据库链接有限,必须加以控制,否则必会报错;

也就是我们要控制,即使开了很多个线程处理上述业务,当时当涉及到数据库存储的时候,一定要控制线程同时访问数据库的数量问题, 那么这就是 Semaphore 的场景了;

假如我这里使用 阻塞队列,可以嘛? 当然也可以,我阻塞队列就是 10 个,那么上述场景也能实现;

2. 编码如下

这里采用 Semaphore 实现,来控制并发访问数据库链接的数量,不超过10个
代码如下(示例):


public class SemaphoreTest {
    private static final int THREAD_COUNT = 30;
    private static ExecutorService threadPool = Executors.newFixedThreadPool(THREAD_COUNT);
    private static Semaphore s = new Semaphore(10);

    public static void main(String[] args) {
        for (int i = 0; i < THREAD_COUNT; i++) {
            threadPool.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        s.acquire();
                        System.out.println("save data");
                        System.out.println(s.availablePermits());
                        //System.out.println(s.drainPermits());
                        s.release();
                        System.out.println(s.availablePermits());
                    } catch (InterruptedException e) {
                    }
                }
            });
        }
        threadPool.shutdown();
    }

}

Semaphore线程信号量_第1张图片

  1. 其实这里的打印结果没有任何意义,因为打印是需要时间的,多线程运行是很快的,所以根本无法准确打印出运行时候的具体顺序
  2. 但是我们可以根据打印结果大致猜测出运行的具体过程:
    就是availablePermits() 是来获取可用的凭证,那么开始运行的时候,随着每个线程都能通过 acquire() 方法获取成功后,此时可用凭证一定是越来越少 10 >> 0;
    由于acquire() 方法是一个阻塞方法,当可用凭证为0之后开始阻塞线程,直到已经获取到凭证的10个线程,其中有一个线程执行了 release(),此时可用凭证变为1 ,然后上一个被阻塞的线程它 调用了acquire(),才会恢复为就绪状态,然后获取凭证,开始执行;
    如此反复,知道最后一个线程开始执行,可用凭证开始从0 >>> 10 增加,直到全部结束,执行完成;

总结

Semaphore的用法也很简单,首先线程使用Semaphore的acquire()方法获取一个许可证,使用完之后调用release()方法归还许可证。其实这里面还有一个重要方法是 tryAcquire(), 他与ReentrantLock 接口中的方法名称一样,功能也是类似的,它不会阻塞,而是会立即返回结果;

你可能感兴趣的:(多线程,数据库,java,开发语言)