首先介绍实现改功能的相关名词:Semaphore,CountDownLatch ,newCachedThreadPool
1.Semaphore

一个计数信号量。顾名思义,一个信号量拥有一定数量的许可证,一个线程acquire需要申请到许可证才能获取锁,否则阻塞等待;释放的时候会返还许可证给阻塞的线程申请。
2.CountDownLatch

CountDownLatch是一个同步的辅助类,它能够使一个线程等待其他线程完成各自的工作后再执行。CountDownLatch是基于AbstractQueuedSynchronizer(AQS)实现的,其通过state作为计数器。构造CountDownLatch时初始化一个state,以后每调用countDown方法一次,state减1;当state=0时,唤醒在await上被挂起的线程。CountDownLatch的计数器state不能被重置,如果需要一种能重置count的版本,可以考虑使用CyclicBarrier。
3.newCachedThreadPool
创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程, 那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。
4.实现源码:
import com.mmall.concurrency.annoations.NotThreadSafe;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
@NotThreadSafe
public class CountExample {
//线程并发数量
private static int threadTotal = 200;
//请求总数
private static int clientTotal = 5000;
private static long count = 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 index = 0; index < clientTotal ; index++) {
executorService.execute(new Runnable() {
public void run() {
try {
//获取一个许可
semaphore.acquire();
add();
//释放一个许可
semaphore.release();
} catch (Exception e) {
e.printStackTrace();
}//计数减一
countDownLatch.countDown();
}
});
}
countDownLatch.await();//阻塞当前线程,直到计时器的值为0
executorService.shutdown();
System.out.println("finished num : " +count);
}
public static void add(){ count++;}
}
5.运行结果如下:

6.结论:从结果看出,几乎每次运行结果都跟实际不相符,都没有达到请求总数5000,说明我们的实现是线程不安全的,下篇我将介绍线程安全的实现方式;。
本文介绍了在Java中使用Semaphore、CountDownLatch和newCachedThreadPool实现多线程计数功能,但指出该实现存在线程不安全问题。在并发环境下,计数结果往往无法达到预期的5000,这提示我们需要采用线程安全的方式来改进计数器的实现。
&spm=1001.2101.3001.5002&articleId=97134836&d=1&t=3&u=7b178b748e894511a326862923f151e0)
3307

被折叠的 条评论
为什么被折叠?



