Java 并发-使用信号量
本文介绍 Java 并发编程中的信号量,如信号量模型的基本介绍,Java 中的 Semaphore 类,以及利用 Semaphore 实现互斥访问和共享锁

# Java 并发-使用信号量

# 信号量简介

  1. Semaphore 也叫信号量、信号灯,是计算机并发编程的一种思想

    信号量模型是由计算机科学家 Dijkstra 在 1965 年提出,一直是并发控制的利器

    直到 1980 年管程模型被提出,越来越多的语言使用管程来实现并发控制

  2. 信号量和管程是可以相互实现的,管程比信号量更容易使用

  3. 信号量模型

    信号量模型由一个计数器、一个等待队列、三个原子操作 init、down、up 组成

    init 设置计数器的初值

    down 操作使计数器减一,若计数器 < 0,则当前线程被阻塞

    up 操作使计数器加一,若计数器 <= 0,则唤醒等待队列的一个线程(> 0 意味着没有线程阻塞)

  4. JDK 中的信号量实现是并发包下的 Semaphore 类

    Semaphore 类构造方法中需要传入初值 int,可选第二参数是 boolean,表示是否使用公平锁

    Semaphore 类的 acquire 和 release 方法分被对应 down 和 up 操作

    Semaphore 其他 API

    • availablePermits:返回计数器当前值

    • drainPermits:将计数器置为 0,并返回减少的值

    • reducePermits:将计数器减少指定值

# 使用 Semaphore

  1. 利用 Semaphore 保证互斥访问

    class Adder {
        static int count;
        //初始化信号量
        static final Semaphore s = new Semaphore(1);
        //用信号量保证互斥    
        static void addOne() {
            try {
                s.acquire();
                count += 1;
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                s.release();
            }
        }
    }
    

    多个线程执行 addOne,一个线程执行 acquire 后计数器为 0,可以继续执行,其他线程 acquire 后计数器小于 0,无法继续执行,只能等待有线程 release 时唤醒一个等待队列中的线程

  2. 利用 Semaphore 实现共享锁

    实现一个简易限流器

    class ObjectPool<T, R> {
        private final List<T> pool = new Vector<>();
        private final Semaphore sem;
        // 构造函数
        ObjectPool(int size, List<T> list) {
            // 添加资源
            pool.addAll(list);
            // 设置可同时访问的线程数量
            sem = new Semaphore(size);
        }
        // 消费池中的对象
        R consume(Function<T, R> func) {
            T t = null;
            try {
                sem.acquire();
                // 使用 Vector 保证原子
                t = pool.remove(0);
                return func.apply(t);
            } catch (Exception e) {
                return null;
            } finally {
                // 消费完成后放回
                pool.add(t);
                sem.release();
            }
        }
    }
    

    多个线程同时从 ObjectPool 中取对象,只有指定数量的线程可以取到,其他线程将等待,ObjectPool 中的对象是重复利用的

Comment here, be cool~

Copyright © 2020 CadeCode

Theme 2zh powered by VuePress

本页访问次数 0

Loading