JUC包下的信号量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... 

应用

  • 使用 Semaphore 限流,在访问高峰期时,让请求线程阻塞,高峰期过去再释放许可,当然它只适合限制单机线程数量,并且仅是限制线程数,而不是限制资源数(例如连接数,请对比 Tomcat LimitLatch 的实现)
  • 用 Semaphore 实现简单连接池,线程数和数据库连接数是相等的
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;
            }
        }
    }
}

原理

AQS使用共享模式,state代表资源数,state大于0才能获得锁,否则进入等待队列。

原创文章,作者:彭晨涛,如若转载,请注明出处:https://www.codetool.top/article/juc%e5%8c%85%e4%b8%8b%e7%9a%84%e4%bf%a1%e5%8f%b7%e9%87%8fsemaphore/

(0)
彭晨涛彭晨涛管理者
上一篇 2020年2月6日
下一篇 2020年2月6日

相关推荐

  • ArrayList源码分析

    总结 总结放前面防止太长不看: ArrayList内部是用数组实现的。 如果使用无参构造函数建立ArrayList,在添加第一个元素的时候会分配10个元素的空间。 ArrayLis…

    2019年11月22日
    0150
  • AbstractCollection默认集合类

    AbstractCollection用于实现基本的Collection结构,提供给普通用户继承使用。也是JDK集合类的父类,部分方法是没有被重载的。 相比Collection接口并…

    Java 2019年11月18日
    0100
  • JUC包下的读写锁ReentrantReadWriteLock以及StampedLock

    ReentrantReadWriteLock 概述 当读操作远远高于写操作时,这时候使用 读写锁 让 读-读 可以并发,提高性能。 类似于数据库中的 select ... from…

    2020年2月5日
    0140
  • 对象的输入输出-Java序列化机制

    对象序列化和反序列化,在Java中体现为两种字节流: ObjectInputStream、ObjectOutputStream 序列化的概念 指堆内存中的java对象数据,通过某种…

    Java 2019年12月21日
    0170
  • Java自动装箱缓存机制

    尝试运行这段代码: 相似的两段代码,得到的结果却完全不相同。 首先要知道在java中==比较的是对象的引用,从直觉出发,无论是integer1、integer2还是integer3…

    Java 2019年12月5日
    0140
  • 基于NIO模型改进多人聊天室

    第一版和第二版见基于BIO模型实现多人聊天室 NIO介绍见: NIO、BIO模型对比实现文件的复制NIO网络编程之Selector介绍 第三版:使用NIO模型改进 服务端实现 pu…

    Java 2020年2月7日
    0190
  • Java8避免空指针异常Optional类的使用

    最近都是一天写一篇算法题解,好久没有写过博客了,不知道写啥了而且快到期末考试了。。 今天介绍一个Java8的特性:Optional类,这个类我平时也不咋用,今天来研究一下。 Opt…

    Java 2020年6月3日
    0660
  • 阻塞队列BlockingQueue详解

    阻塞队列是生产者消费者模式的经典体现。 我们在曾在Java线程池详解中自己实现过一个阻塞队列,这篇文章我们来研究一下JDK中的阻塞队列: BlockingQueue接口主要方法 抛…

    Java 2020年2月14日
    0580
  • 线程内部的run方法可以向外抛出异常吗?

    提出问题 线程的run方法向外可以抛出异常吗,或者能被主线程捕获异常吗?比如下面这段代码: public static void main(String[] args) { try…

    Java 2020年5月20日
    06520
  • 为什么说Java只有值传递?

    先说一下。。以后可能不会怎么写Java相关的博客了,因为找到了字节跳动的实习工作,用Go/Python开发后端,所以这几天在抓紧时间学Go,在学Go的时候,了解到Go语言只有值传递…

    Java 2020年6月26日
    03100

发表回复

登录后才能评论