试验目的
本文的目的是对比Go协程、Java线程池、Java虚拟线程的切换成本,所以计算任务本身是一个空函数。
版本
$ java --version
java 22 2024-03-19
$ go version
go version go1.22.0 windows/amd64
试验代码
go代码
package main
import (
"fmt"
"os"
"strconv"
"sync"
"time"
)
// 任务本身
func task() {
}
func main() {
// 不需要热身
p, _ := strconv.Atoi(os.Args[1]) //p个并发协程
fmt.Printf("concurrency %d\n", p)
wg := sync.WaitGroup{}
wg.Add(p)
begin := time.Now() //开始计时
for i := 0; i < p; i++ {
go func() {
defer wg.Done()
task() // 每个协程执行一次任务
}()
}
wg.Wait() //等所有任务结束
useTime := time.Since(begin).Milliseconds() //结束计时
fmt.Printf("use time %d ms\n", useTime)
}
// go build -o basic/VirtualThread/matmul.exe basic/VirtualThread/mat_mul.go
// ./basic/VirtualThread/matmul.exe 10000
java代码
package basic.VirtualThread;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class MatMulVT{
// 任务本身
private static Runnable runnable = () -> {
};
public static void awaitTerminationAfterShutdown(ExecutorService threadPool) {
threadPool.shutdown();
try {
if (!threadPool.awaitTermination(60, TimeUnit.SECONDS)) {
threadPool.shutdownNow();
}
} catch (InterruptedException ex) {
threadPool.shutdownNow();
Thread.currentThread().interrupt();
}
}
public static void main(String[] args) {
// 热身,第一次不算
int p=Integer.parseInt(args[0]);//p个并发线程
System.out.println("concurrency "+p);
// ExecutorService executorService = Executors.newScheduledThreadPool(10);//定长线程池
ExecutorService executorService = Executors.newVirtualThreadPerTaskExecutor(); //为每个任务创建一个虚拟线程
for (int i = 0; i < 10000; i++) { //JIT warm up 1万次
executorService.submit(runnable);
}
try{TimeUnit.SECONDS.sleep(1);}catch(InterruptedException e){}; //等热身任务线束
// 第二次才算。复用ExecutorService
long startTime = System.currentTimeMillis();//开始计时
for (int i = 0; i < p; i++) {
executorService.submit(runnable);
}
awaitTerminationAfterShutdown(executorService);//等所有任务结束
long endTime = System.currentTimeMillis();//结束计时
System.out.println("use time "+(endTime-startTime)+" ms");
}
}
// javac -encoding UTF-8 basic/VirtualThread/MatMulVT.java
// java basic/VirtualThread/MatMulVT 10000
java传统线程池和虚拟线程仅仅是第29行和30行的区别。
试验结果
结果分析
- java虚拟线程确实比线程池快很多。
- go协程比java虚拟线程更轻,切换速度更快。
- go协程切换成本跟并发数保持了非常好的线性关系,并发数从1万->10万->100万,总耗时也是按10倍增长。
进阶内容
enjoy your golang travels! 如需快速、高效、深入的学习Go语言,欢迎试听我录制的golang视频课程《golang从入门到通天》(电脑端按Ctrl++放大页面观看)