1. 程式人生 > >go協程goroutine與Java多執行緒比較

go協程goroutine與Java多執行緒比較

引言:

個人理解的執行緒,協程和單,多核執行緒

  1. 單核CPU上執行的多執行緒程式, 同一時間只能一個執行緒在跑, 系統幫你切換執行緒而已(cpu時間切片), 系統給每個執行緒分配時間片來執行, 每個時間片大概10ms左右, 看起來像是同時跑, 但實際上是每個執行緒跑一點點就換到其它執行緒繼續跑,效率不會有提高的,切換執行緒反倒會增加開銷(執行緒的上下文切換),巨集觀的可看著並行,單核裡面只是併發,真正執行的一個cpu核心只在同一時刻執行一個執行緒(不是程序)。
  2. 多執行緒的用處在於,做某個耗時的操作時,需要等待返回結果,這時用多執行緒可以提高程式併發程度。如果一個不需要任何等待並且順序執行能夠完成的任務,用多執行緒是十分浪費的。
  3. 個人見解,對於Thread Runable以及ThreadPoolExcutor等建立的執行緒,執行緒池內部是使用單核心執行(偽並行的併發多執行緒),而jdk1.7中提供的fork/join併發框架,使用的是多核心任務切分執行,個人覺得和map/reduce有一定類似之處。
  4. 協程是一種使用者態的輕量級執行緒,協程的排程完全由使用者控制。而執行緒的排程是作業系統核心控制的,通過使用者自己控制,可減少上下文頻繁切換的系統開銷,提高效率。

環境:

  1. 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在併發多工處理能力上有著與生俱來的優勢。

後記