之前写了一篇文章关于四种线程池的解析。
但是对于 FixedThreadPool 与 CachedThreadPool 适用的场景其实还是比较模糊难以界定的。所以笔者今天通过设计大任务并发和小任务并发来验证 FixedThreadPool 与 CachedThreadPool 的适用场景。
首先我设计了一个任务基类,它通过计算圆周率来模拟 cpu 的密集计算、通过写日志到本地文件来模拟 IO。
这两个方法都通过参数 n 来调整任务的大小规模。
public class Task {
/**
* 通过计算圆周率模拟cpu计算
* 通过公式 π=4*(1-1/3+1/5-1/7+1/9-1/11+....)
*
* @return
*/
public static double calculatePI(long n) {
double item = 0.0;
double sum = 0;
int flag = -1;
for (int i = 0; i <= n; i++) {
flag *= -1;
item = flag * 1.0 / (2 * i + 1);
sum += item;
}
return sum * 4;
}
/**
* 通过写日志模拟IO操作
* @param n
*/
public static void writeIO(int n) {
try {
Date date = new Date();
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss-SSS");
String fileName = Thread.currentThread().getName() + "-" + format.format(date) + ".log";
FileOutputStream os = new FileOutputStream("C:\\Users\\valarchie\\Desktop\\logs\\" + fileName);
for (int i = 0; i < n; i++) {
os.write(("写入日志" + i + "次").getBytes());
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
在笔者的设计当中大任务的规模是小任务的 10 倍,具体请看代码:
大任务
public class BigTask extends Task implements Runnable {
private CountDownLatch latch;
public BigTask(CountDownLatch latch) {
this.latch = latch;
}
public static double calculatePI() {
return calculatePI(100000000);
}
public static void writeIO() {
writeIO(100);
}
@Override
public void run() {
calculatePI();
writeIO();
latch.countDown();
}
}
小任务:
public class SmallTask extends Task implements Runnable {
private CountDownLatch latch;
public SmallTask(CountDownLatch latch) {
this.latch = latch;
}
public static double calculatePI() {
return calculatePI(10000000);
}
public static void writeIO() {
writeIO(10);
}
@Override
public void run() {
calculatePI();
writeIO();
latch.countDown();
}
}
通过测试我们得出一个小任务的运行时间大概在 86ms 左右。一个大任务的运行时间大概在 575ms 左右。
接下来我们分别测试 100 个大任务和 100 个小任务分别在单线程、FixedThreadPool、CachedThreadPool 三种情况下的运行时间(我的笔记本是 4 核的,经过简单测试 FixedThreadPool 在 16 线程数的情况下性能最优良)。
我们使用 CountDownLatch 的计算多线程的运行时间,以下是多线程的测试代码模板:
public static void main(String[] args) {
int taskCount = 100;
CountDownLatch latch = new CountDownLatch(taskCount);
ExecutorService executorService = Executors.newFixedThreadPool(16);
long t1 = System.currentTimeMillis();
for (int i = 0; i < taskCount; i++) {
executorService.submit(new SmallTask(latch));
}
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
long t2 = System.currentTimeMillis();
// 得出多线程的运行时间
System.out.println(t2 - t1);
executorService.shutdown();
}
任务模型*100 | 单线程 | FixedThreadPool | CachedThreadPool |
---|---|---|---|
大任务 | 45067ms | 6613ms | 6224ms |
小任务 | 4754ms | 722ms | 726ms |
通过统计发现多线程的性能比单线程的性能优异很多,但是其实 FixedThreadPool 和 CachedThreadPool 的性能差异是差不多相等的并没有比较大差别。
为了更严谨一点,我们控制任务方法的规模和任务数量的规模再进行一次测试
任务模型*100 | FixedThreadPool | CachedThreadPool |
---|---|---|
大任务方法规模*10 | 78738ms | 79669ms |
大任务数量规模*10 | 73654ms | 69343ms |
结果发现其实性能上还是没有差异。
笔者经过验证得出的结论是两种线程池其实在性能上没有非常大差别,但是 FixedThreadPool 可以控制线程的并发数量,而 CachedThreadPool 不能控制线程的并发数量。如果线程数量爆发增长的话对系统会带来危害。个人认为使用 FixedThreadPool 会更好。
笔者认知水平有限,如有错误恳请评论区指正。
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于