并发编程工具 - Semaphore信号量

目录

信号量模型

juc Semaphore结构

使用Semaphore实现限流器


    早在1965就提出了信号量作为解决并发编程的解决方案,直到1980年之后才使用管程模型来解决并发问题(Java选择了MESA管程模型),并且管程能实现的功能使用信号量都能实现,基本所有的编程语言都支持信号量的方式,而Java中juc Semaphore就是该实现。作为非科班出生,自己认为了解并发编程的历史非常的重要。

信号量模型

    一个计数器,一个等待队列,三个方法。信号量模型里,计数器和等待队列对外是透明的,所以只能通过信号量模型提供的三个方法来访问它们,这三个方法分别是:init()、down() 和 up()

init():设置计数器的初始值。

down():计数器的值减 1;如果此时计数器的值小于 0,则当前线程将被阻塞,否则当前线程可以继续执行。

up():计数器的值加 1;如果此时计数器的值小于或者等于 0,则唤醒等待队列中的一个线程,并将其从等待队列中移除。

 

juc Semaphore结构

    Semaphore与ReentrantLock一样的结果,属性Sync实现自AQS,都实现了公平和非公平锁两种,但是刚好一个是AQS的独占模式,Semaphore是共享模式。

public class Semaphore implements java.io.Serializable {
    
    /** 默认构造实现非公平锁 */
    private final java.util.concurrent.Semaphore.Sync sync;

    public Semaphore(int permits) {
        sync = new java.util.concurrent.Semaphore.NonfairSync(permits);
    }
    
    public Semaphore(int permits, boolean fair) {
        sync = fair ? new FairSync(permits) : new NonfairSync(permits);
    }
}

    无论公平锁非公平锁,初始化时都要设置初始值(即上面的init方法需要设置初始信号量值),而当前我们设置的初始化,直接设置为了AQS的state变量。

static final class NonfairSync extends Sync { // Semaphore 非公平锁
 
    NonfairSync(int permits) {
        super(permits); // super就是Semaphore.Sync
    }
}

abstract static class Sync extends AbstractQueuedSynchronizer {
    Sync(int permits) {
        setState(permits); // 父类Semaphore Sync
    }
}

public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer
        implements java.io.Serializable {

    private volatile int state;

    protected final void setState(int newState) {
        state = newState; // 父类AQS,直接将值赋给state
    }
}

再看看信号量模型的up和down方法,在Semaphore中的实现:

up -> acquire* -> LockSupport#park挂起线程

    直接调用AQS的acquireSharedInterruptibly方法,参见:并发编程理论 - AQS源码分析#acquireShared【获取共享锁,阻塞剩余资源数的线程】

// 每次加1
public void acquire() throws InterruptedException {
    sync.acquireSharedInterruptibly(1);
}
// 可以自定义加的数
public void acquire(int permits) throws InterruptedException {
    if (permits < 0) throw new IllegalArgumentException(); // 我们自定义的步数只能是整数
    sync.acquireSharedInterruptibly(permits);
}

down -> release* -> LockSupport#unpark唤醒线程执行任务

    直接调用AQS的releaseShared方法,参见:并发编程理论 - AQS源码分析#releaseShared【释放共享锁,唤醒管程的可用节点线程】

public void release() {
    sync.releaseShared(1);
}
public void release(int permits) {
    if (permits < 0) throw new IllegalArgumentException(); // 我们自定义的步数只能是整数
    sync.releaseShared(permits);
}

 

使用Semaphore实现限流器

    限流器就是共享模式的实现,比如同时允许3个线程执行任务,前提是有任务的情况下,如果没有任务则需要将线程挂起,否则唤醒对应的线程。

/**
 *  使用{@link Semaphore} 实现一个限流器
 *   即多线程的并发的情况下,只允许指定长度的个数执行任务,其余的排队
 *
 * .......................省略.........................
 * 当前线程{test-thread-93}获取到的任务为:kevin test 2
 * 当前线程{test-thread-94}获取到的任务为:kevin test 3
 * 当前线程{test-thread-95}获取到的任务为:kevin test 4
 * 当前线程{test-thread-96}获取到的任务为:kevin test 5
 * 当前线程{test-thread-97}获取到的任务为:kevin test 6
 * 当前线程{test-thread-98}获取到的任务为:kevin test 7
 * 当前线程{test-thread-99}获取到的任务为:kevin test 8
 * 当前线程{test-thread-100}获取到的任务为:kevin test 9
 *
 * @author kevin
 * @date 2020/7/30 22:32
 * @since 1.0.0
 */
public class SemaphoreLimiter<T, R> {

    private final List<T> pool;

    private final Semaphore semaphore;

    /**
     *  限流器构造
     * @param t 限流类型
     */
    public SemaphoreLimiter(T[] t) {
        int size = t.length;
        this.pool = new Vector<T>(){};
        for (int i = 0; i < size; i++) {
            pool.add(t[i]);
        }
        this.semaphore = new Semaphore(size);
    }

    /**
     *  执行任务
     * @param function 功能
     * @return
     * @throws InterruptedException
     */
    R execute(Function<T, R> function) throws InterruptedException {
        T t = null;
        semaphore.acquire();
        try {
            t = pool.remove(0);
            return function.apply(t);
        } finally {
            pool.add(t);
            semaphore.release();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        String[] str = new String[10];
        for (int i = 0; i < str.length; i++) {
            str[i] = "kevin test " + i;
        }
        SemaphoreLimiter<String, String> pool = new SemaphoreLimiter<String, String>(str);

        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(100, 100,
                0, TimeUnit.MILLISECONDS, new LinkedBlockingDeque<>(), new DefaultThreadFactory("test"));
        for (int i = 0; i < 100; i++) {
            threadPoolExecutor.execute(() -> {
                    try {
                        pool.execute(t -> {
                            System.out.println("当前线程{" + Thread.currentThread().getName() + "}获取到的任务为:" + t);
                            return t;
                        });
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            );
        }
    }

}

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值