写这篇博客的目的是增加自己记忆,方便平常使用
1.信号量
Semaphore 是一种基于计数的信号量,它可以是一个阀值,基于此,多个线程竞争获取许可信号,超过阀值后,线程申请许可信号将会别阻塞
常用方法
方法 | 含义 |
---|---|
acquire() | 申请一个信号量获取可用的资源,如果可用, 信号量内部的资源个数减掉1,如果没有可用资源线程会阻塞在该方法中,不能结束该方法,不能返回,直到有可用的资源为止 |
release() | 释放资源,释放后信号量内部的资源个数会增加1,果有被阻塞的线程,释放后会唤醒一个线程去获取资源,acquire() 和 release()要成对使用,一般release()放在finally代码块中 |
availablePermits() | 当前可用的资源个数, permits - availablePermits() = 正在使用的资源个数 |
结合CountDownLatch 闭锁 使用方式如下:
@Slf4j
public class CountExample {
//同时并发执行的总数
private static int threadTotal = 200;
//请求总数
private static int clientTotal = 5000;
//验证实际请求的总数 会自增1
private static int count = 0;
public static void main(String[] args) throws Exception{
//创建一个可缓存的线程池,如果线程长度超过处理需要,可灵活回收线程,若无可回收,则新建线程
ExecutorService executor = Executors.newCachedThreadPool();
final Semaphore semaphore = new Semaphore(threadTotal);
final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
for (int i = 0; i < clientTotal; i++) {
executor.execute(() -> {
try {
//得到一个信号量
semaphore.acquire();
add();
//释放信号量
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
log.error("exception",e);
}
// 减 1
countDownLatch.countDown();
});
}
//阻塞,对计数没任何影响
countDownLatch.await();
//关闭线程池
executor.shutdown();
log.info("完成count:{}", count);
}
private static void add(){
count++;
}
}
返回结果
[main] INFO com.mmall.concurrency.mufeng.basic.CountExample - 完成count:4970
期望值是5000,实际值为 4970,由此可见这个执行方法是线程不安全的。
下面这个是线程安全写法:采用AtomicInteger
@Slf4j
@ThreadSafe
public class CountExample2 {
// 请求总数
public static int clientTotal = 5000;
// 同时并发执行的线程数
public static int threadTotal = 200;
public static AtomicInteger count = new AtomicInteger(0);
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newCachedThreadPool();
final Semaphore semaphore = new Semaphore(threadTotal);
final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
for (int i = 0; i < clientTotal ; i++) {
executorService.execute(() -> {
try {
semaphore.acquire();
add();
semaphore.release();
} catch (Exception e) {
log.error("exception", e);
}
countDownLatch.countDown();
});
}
countDownLatch.await();
executorService.shutdown();
log.info("count:{}", count.get());
}
private static void add() {
count.incrementAndGet();
// count.getAndIncrement();
}
}
执行结果:
[main] INFO com.mmall.concurrency.example.count.CountExample2 - count:5000
因为采用线程安全类 AtomicInteger 所以在自增1运算时,可以保证线程安全