go協程goroutine與Java多執行緒比較
阿新 • • 發佈:2019-02-03
引言:
個人理解的執行緒,協程和單,多核執行緒
1. 單核CPU上執行的多執行緒程式, 同一時間只能一個執行緒在跑, 系統幫你切換執行緒而已(cpu時間切片), 系統給每個執行緒分配時間片來執行, 每個時間片大概10ms左右, 看起來像是同時跑, 但實際上是每個執行緒跑一點點就換到其它執行緒繼續跑,效率不會有提高的,切換執行緒反倒會增加開銷(執行緒的上下文切換),巨集觀的可看著並行,單核裡面只是併發,真正執行的一個cpu核心只在同一時刻執行一個執行緒(不是程序)。
2. 多執行緒的用處在於,做某個耗時的操作時,需要等待返回結果,這時用多執行緒可以提高程式併發程度。如果一個不需要任何等待並且順序執行能夠完成的任務,用多執行緒是十分浪費的。
3. 個人見解,對於Thread Runable以及ThreadPoolExcutor等建立的執行緒,執行緒池內部是使用單核心執行(偽並行的併發多執行緒),而jdk1.7中提供的fork/join併發框架,使用的是多核心任務切分執行,個人覺得和map/reduce有一定類似之處。
4. 協程是一種使用者態的輕量級執行緒,協程的排程完全由使用者控制。而執行緒的排程是作業系統核心控制的,通過使用者自己控制,可減少上下文頻繁切換的系統開銷,提高效率。
環境:
- ubuntu 16.04 LTS
測試過程
對比程式,系統發生1千萬次併發,併發為一個無操作的空函式,使用time指令對比效能
java完整版
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* Created by zhoudazhuang on 14-8-15.
* jdk1.7以及一下
* new Runnable() {
@Override
public void run() {
}
jdk1.8可使用lambda
() -> {}
* 1. javac Main.java
* 2. java Main(java二進位制位元組碼,這樣就可以運行了 不過這裡再準備打包成執行jar包)
* 3. jar -cvf my.jar *.class
* 4. time java -server -jar my.jar my.jar中沒有主清單屬性 修改壓縮jar中MANIFEST.MF新增Main-Class:要空一格Main(主類class名字,有包加包 或者第二種方法直接在引數中指定)
* 5. 改好後使用 time java -server -jar my.jar 或者直接使用time java -cp ./my.jar Main //主要不要用-jar了 -cp 目錄和zip/jar檔案的類搜尋路徑 直接指定就可以了
* 輸出
* # time java -server -jar my.jar
* elapsed time: 0.041s
* java -server -jar my.jar 0.26s user 0.01s system 215% cpu 0.126 total
* time ls; time java是linux命令 不用-server我感覺輸出也一樣
*/
public class Main {
private static final int TIMES = 100 * 1000 * 100;
public static void main(String[] args) throws Exception {
ExecutorService service = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
long t1 = System.currentTimeMillis();
for (int i=0;i<TIMES;i++) {
service.submit(new Runnable() {
@Override
public void run() {
}
});
}
service.shutdown();
long t2 = System.currentTimeMillis();
System.out.printf("elapsed time: %.3fs\n", (t2-t1)/1000f);
}
}
輸出
# time java -cp ./my.jar Main
elapsed time: 14.589s
java -cp ./my.jar Main 75.20s user 2.96s system 486% cpu 16.072 total
golang完整版
//對比程式,系統發生1億次併發,併發為一個無操作的空函式,使用time指令對比效能
package main
import (
"runtime"
"fmt"
"time"
)
const (
TIMES = 100 * 1000 * 100
)
func main() {
runtime.GOMAXPROCS(runtime.NumCPU())
fmt.Println("CPUs:", runtime.NumCPU(), "Goroutines:", runtime.NumGoroutine())
t1 := time.Now()
for i:=0; i<TIMES; i++ {
go func() {}()
}
for runtime.NumGoroutine() > 4 {
//fmt.Println("current goroutines:", runtime.NumGoroutine())
//time.Sleep(time.Second)
}
t2 := time.Now()
fmt.Printf("elapsed time: %.3fs\n", t2.Sub(t1).Seconds())
}
執行結果:
CPUs: 8 Goroutines: 1
elapsed time: 3.582s
./compareJava 13.09s user 1.49s system 405% cpu 3.591 total
由於多執行緒的處理機制不同,java的處理時間,cpu負載等明顯高與goroutine,所以在此種情況下,goroutine在併發多工處理能力上有著與生俱來的優勢。