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后调用。