Semaphore信号量

java.util.concurrent简称JUC,日常总结

Semaphore 信号量

Semaphore 信号量 英[ˈseməfɔː®],Semaphore可以控制同时访问的线程个数,通过acquire()方法获取一个许可,如果没有就等待,通过release()可以释放一个许可。

构造方法
    /**
     * 使用指定数量许可和非公平规则创建一个信号量
     *
     * @param permits 指定信号量的数量
     */
    public Semaphore(int permits) {
        sync = new Nonfai
    }
	/**
     * 使用指定数量许可和指定公平规则创建一个信号量
     *
     * @param permits 指定信号量的数量
     * @param fair 是否是公平的,即等待时间越久的越先获取许可
     */
    public Semaphore(int permits, boolean fair) {
        sync = fair ? new FairSync(permits) : new NonfairSync(permits);
    }
常用方法
	/**
     * 尝试获取一个许可,如果获取成功,则立即返回true。如果获取失败,则立即返回false。如果没有许可可以获得,则会一直等待,直到获得许可。
     */
    public void acquire() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }
    /**
     * 尝试获取permits个许可,如果获取成功,则立即返回true。如果获取失败,则立即返回false。如果没有许可可以获得,则会一直等待,直到获得许可。
     * @param permits 信号量的数量
     */
    public void acquire(int permits) throws InterruptedException {
        if (permits < 0) throw new IllegalArgumentException();
        sync.acquireSharedInterruptibly(permits);
    }
	/**
     * 释放1个许可证,并发回给信号机。将可用许可证数量增加1。
     */
    public void release() {
        sync.releaseShared(1);
    }
    /**
     * 尝试获取一个许可,若获取成功,则立即返回true,若获取失败,则立即返回false
     */
    public boolean tryAcquire(int permits) {
        if (permits < 0) throw new IllegalArgumentException();
        return sync.nonfairTryAcquireShared(permits) >= 0;
    }
	/**
     * 尝试获取一个许可,若在指定的时间内获取成功,则立即返回true,否则则立即返回false
     */
    public boolean tryAcquire(int permits, long timeout, TimeUnit unit)
        throws InterruptedException {
        if (permits < 0) throw new IllegalArgumentException();
        return sync.tryAcquireSharedNanos(permits, unit.toNanos(timeout));
    }
	/**
     * 尝试获取permits个许可,若获取成功,则立即返回true,若获取失败,则立即返回false
     */
    public boolean tryAcquire(int permits) {
        if (permits < 0) throw new IllegalArgumentException();
        return sync.nonfairTryAcquireShared(permits) >= 0;
    }
/**
     * 尝试获取permits个许可,若在指定的时间内获取成功,则立即返回true,否则则立即返回false
     */
    public boolean tryAcquire(int permits, long timeout, TimeUnit unit)
        throws InterruptedException {
        if (permits < 0) throw new IllegalArgumentException();
        return sync.tryAcquireSharedNanos(permits, unit.toNanos(timeout));
    }
	/**
     * 返回此信号量中当前可用的许可证数。
     * @return 可用的许可数目
     */
    public int availablePermits() {
        return sync.getPermits();
    }
举一个简单的例子

通过Semaphore信号量来实现。我们的工厂一共就有5台机器可以同时生产材料。一台机器同时只能被1个工人使用,只有这个工人使用完了,其他工人才能继续使用。

package com.test.juc;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.Semaphore;

/**
 * @author liao
 * @version 1.0.0
 * @description
 * @date 2022/3/10 14:33
 **/
@Slf4j
public class TestSemaphore {
    public static void main(String[] args) {
        int N = 8;            //工人数
        Semaphore semaphore = new Semaphore(5); //机器数目
        for (int i = 0; i < N; i++) {
            new Worker(i, semaphore).start();
        }
        log.info("END");
    }

    static class Worker extends Thread {
        private int num;
        private Semaphore semaphore;

        public Worker(int num, Semaphore semaphore) {
            this.num = num;
            this.semaphore = semaphore;
        }

        @Override
        public void run() {
            try {
                semaphore.acquire();
                log.info("工人" + this.num + "占用一个机器在生产...");
                Thread.sleep(2000);
                log.info("工人" + this.num + "释放出机器");
                semaphore.release();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
执行结果
14:14:15.240 [Thread-4] INFO com.test.juc.TestSemaphore - 工人4占用一个机器在生产...
14:14:15.239 [Thread-1] INFO com.test.juc.TestSemaphore - 工人1占用一个机器在生产...
14:14:15.239 [Thread-3] INFO com.test.juc.TestSemaphore - 工人3占用一个机器在生产...
14:14:15.240 [Thread-2] INFO com.test.juc.TestSemaphore - 工人2占用一个机器在生产...
14:14:15.240 [main] INFO com.test.juc.TestSemaphore - END
14:14:15.240 [Thread-0] INFO com.test.juc.TestSemaphore - 工人0占用一个机器在生产...
14:14:17.257 [Thread-4] INFO com.test.juc.TestSemaphore - 工人4释放出机器
14:14:17.257 [Thread-1] INFO com.test.juc.TestSemaphore - 工人1释放出机器
14:14:17.257 [Thread-0] INFO com.test.juc.TestSemaphore - 工人0释放出机器
14:14:17.257 [Thread-5] INFO com.test.juc.TestSemaphore - 工人5占用一个机器在生产...
14:14:17.257 [Thread-2] INFO com.test.juc.TestSemaphore - 工人2释放出机器
14:14:17.257 [Thread-6] INFO com.test.juc.TestSemaphore - 工人6占用一个机器在生产...
14:14:17.257 [Thread-3] INFO com.test.juc.TestSemaphore - 工人3释放出机器
14:14:17.258 [Thread-7] INFO com.test.juc.TestSemaphore - 工人7占用一个机器在生产...
14:14:19.264 [Thread-7] INFO com.test.juc.TestSemaphore - 工人7释放出机器
14:14:19.264 [Thread-6] INFO com.test.juc.TestSemaphore - 工人6释放出机器
14:14:19.264 [Thread-5] INFO com.test.juc.TestSemaphore - 工人5释放出机器
实战
工具类
package com.test.juc;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

/**
 * 线程信号量
 *
 * @author tengbx
 */
public class ThreadSemaphoreUtil {
    private final static Logger LOGGER = LoggerFactory.getLogger(ThreadSemaphoreUtil.class);

    /**
     * 线程执行信号量
     */
//	public static final int THREAD_SEMAPHORE= Integer.parseInt(PropertyUtil.getProperty("THREAD_SEMAPHORE", "/config/properties/system.properties"));
    public static final int THREAD_SEMAPHORE = 3;
    //信号量
    static Semaphore SEMAPHORE = new Semaphore(THREAD_SEMAPHORE, true);
    //线程池
    static ExecutorService pool = Executors.newCachedThreadPool();
    static long start = System.currentTimeMillis();

    //重新执行
    public static void restart() {
        SEMAPHORE = new Semaphore(THREAD_SEMAPHORE, true);
        pool = Executors.newCachedThreadPool();
        start = System.currentTimeMillis();
    }

    //执行线程
    public static void execute(Runnable runnable) throws InterruptedException {
        pool.execute(runnable);
    }

    //等待全部线程池任务执行完成
    public static void allDone() throws InterruptedException {
        pool.shutdown();
        int i = 0;
        while (true) {
            LOGGER.info("allDone当前系统的并发数是:" + (THREAD_SEMAPHORE - SEMAPHORE.availablePermits()));
            // 线程池关闭后所有任务执行完成 && 设置线程数-信号量中的可用的令牌数量==0
            if (pool.isTerminated() && THREAD_SEMAPHORE - SEMAPHORE.availablePermits() == 0) {
                LOGGER.info("allDone所有的子线程都结束了");
                break;
            }
            if (++i > 60) {
                Thread.sleep(6000);
            } else if (++i > 30) {
                Thread.sleep(3000);
            } else {
                Thread.sleep(1000);
            }
        }
    }

    public static void acquire() throws InterruptedException {
        SEMAPHORE.acquire();//获取信号灯许可
        LOGGER.info("获取信号灯许可");
    }

    public static void release() {
        SEMAPHORE.release();//释放信号灯
        LOGGER.info("释放信号灯");
    }

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 10; i++) {
            final int j = i;
            Runnable runnable = () -> {
                try {
                    ThreadSemaphoreUtil.acquire();//获取信号灯许可
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                try {
                    Thread.sleep(new Random().nextInt(10000));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                ThreadSemaphoreUtil.release();//释放信号灯
            };
            ThreadSemaphoreUtil.execute(runnable);
        }
        ThreadSemaphoreUtil.allDone();
    }
}
执行结果
14:28:35.300 [pool-1-thread-4] INFO com.test.juc.ThreadSemaphoreUtil - 获取信号灯许可
14:28:35.301 [main] INFO com.test.juc.ThreadSemaphoreUtil - allDone当前系统的并发数是:3
14:28:35.300 [pool-1-thread-1] INFO com.test.juc.ThreadSemaphoreUtil - 获取信号灯许可
14:28:35.301 [pool-1-thread-3] INFO com.test.juc.ThreadSemaphoreUtil - 获取信号灯许可
14:28:35.947 [pool-1-thread-3] INFO com.test.juc.ThreadSemaphoreUtil - 释放信号灯
14:28:35.947 [pool-1-thread-2] INFO com.test.juc.ThreadSemaphoreUtil - 获取信号灯许可
14:28:36.316 [main] INFO com.test.juc.ThreadSemaphoreUtil - allDone当前系统的并发数是:3
14:28:36.603 [pool-1-thread-5] INFO com.test.juc.ThreadSemaphoreUtil - 获取信号灯许可
14:28:36.603 [pool-1-thread-2] INFO com.test.juc.ThreadSemaphoreUtil - 释放信号灯
14:28:37.317 [main] INFO com.test.juc.ThreadSemaphoreUtil - allDone当前系统的并发数是:3
14:28:38.317 [main] INFO com.test.juc.ThreadSemaphoreUtil - allDone当前系统的并发数是:3
14:28:38.989 [pool-1-thread-5] INFO com.test.juc.ThreadSemaphoreUtil - 释放信号灯
14:28:38.989 [pool-1-thread-6] INFO com.test.juc.ThreadSemaphoreUtil - 获取信号灯许可
14:28:39.317 [main] INFO com.test.juc.ThreadSemaphoreUtil - allDone当前系统的并发数是:3
14:28:40.317 [main] INFO com.test.juc.ThreadSemaphoreUtil - allDone当前系统的并发数是:3
14:28:40.578 [pool-1-thread-9] INFO com.test.juc.ThreadSemaphoreUtil - 获取信号灯许可
14:28:40.578 [pool-1-thread-1] INFO com.test.juc.ThreadSemaphoreUtil - 释放信号灯
14:28:41.318 [main] INFO com.test.juc.ThreadSemaphoreUtil - allDone当前系统的并发数是:3
14:28:42.318 [main] INFO com.test.juc.ThreadSemaphoreUtil - allDone当前系统的并发数是:3
14:28:43.012 [pool-1-thread-6] INFO com.test.juc.ThreadSemaphoreUtil - 释放信号灯
14:28:43.012 [pool-1-thread-10] INFO com.test.juc.ThreadSemaphoreUtil - 获取信号灯许可
14:28:43.158 [pool-1-thread-9] INFO com.test.juc.ThreadSemaphoreUtil - 释放信号灯
14:28:43.158 [pool-1-thread-7] INFO com.test.juc.ThreadSemaphoreUtil - 获取信号灯许可
14:28:43.319 [main] INFO com.test.juc.ThreadSemaphoreUtil - allDone当前系统的并发数是:3
14:28:43.598 [pool-1-thread-4] INFO com.test.juc.ThreadSemaphoreUtil - 释放信号灯
14:28:43.598 [pool-1-thread-8] INFO com.test.juc.ThreadSemaphoreUtil - 获取信号灯许可
14:28:44.319 [main] INFO com.test.juc.ThreadSemaphoreUtil - allDone当前系统的并发数是:3
14:28:45.304 [pool-1-thread-7] INFO com.test.juc.ThreadSemaphoreUtil - 释放信号灯
14:28:45.321 [main] INFO com.test.juc.ThreadSemaphoreUtil - allDone当前系统的并发数是:2
14:28:46.321 [main] INFO com.test.juc.ThreadSemaphoreUtil - allDone当前系统的并发数是:2
14:28:47.321 [main] INFO com.test.juc.ThreadSemaphoreUtil - allDone当前系统的并发数是:2
14:28:48.322 [main] INFO com.test.juc.ThreadSemaphoreUtil - allDone当前系统的并发数是:2
14:28:48.857 [pool-1-thread-8] INFO com.test.juc.ThreadSemaphoreUtil - 释放信号灯
14:28:49.323 [main] INFO com.test.juc.ThreadSemaphoreUtil - allDone当前系统的并发数是:1
14:28:50.323 [main] INFO com.test.juc.ThreadSemaphoreUtil - allDone当前系统的并发数是:1
14:28:50.855 [pool-1-thread-10] INFO com.test.juc.ThreadSemaphoreUtil - 释放信号灯
14:28:53.323 [main] INFO com.test.juc.ThreadSemaphoreUtil - allDone当前系统的并发数是:0
14:28:53.323 [main] INFO com.test.juc.ThreadSemaphoreUtil - allDone所有的子线程都结束了
总结

Semaphore信号量,用于管理一组资源。其内部是基于AQS的共享模式,AQS的状态标识许可证的数量,在许可证数量不够时,线程将会被挂起。而一旦有一个线程释放一个资源,那么就有可能重新唤醒等待队列中的线程继续执行。主要作用是控制线程的执行。

问题总结
Semaphore初始化令牌数量可以为负数吗?

可以。源码中指出,初始化令牌数可以为负数,此时线程获取令牌会进入阻塞状态,需要调用release方法添加令牌。

semaphore初始化有10个令牌,11个线程同时各调用1次acquire方法,会发生什么?

拿不到令牌的线程阻塞,不会继续往下运行。

semaphore初始化有10个令牌,一个线程重复调用11次acquire方法,会发生什么?

线程阻塞,不会继续往下运行。可能你会考虑类似于锁的重入的问题,很好,但是,令牌没有重入的概念。你只要调用一次acquire方法,就需要有一个令牌才能继续运行。

semaphore初始化有1个令牌,1个线程调用一次acquire方法,然后调用两次release方法,之后另外一个线程调用acquire(2)方法,此线程能够获取到足够的令牌并继续运行吗?

能,原因是release方法会添加令牌,并不会以初始化的大小为准。

semaphore初始化有2个令牌,一个线程调用1次release方法,然后一次性获取3个令牌,会获取到吗?

能,原因是release会添加令牌,并不会以初始化的大小为准。Semaphore中release方法的调用并没有限制要在acquire后调用。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值