================编辑2016 05 27 16:55已解决===================
这个问题解决了!感谢@Paul Hankin,你说得对!Java擅长做"for(){i=i+1}",所以当我把代码改为"for((){id=i+i}"时,Java就输了。
(附言:使用Java ExecutorService确实让Java结果很好,但仍然不如goroutine,这里没有ExecutorServices的例子)
Java代码:
import java.util.ArrayList;
import java.util.List;
public class Test {
public static void main(String args[]) throws InterruptedException {
List<Thread> threads = new ArrayList<Thread>();
for(int i = 0; i < 10; i ++) {
threads.add(new Thread(new RunableAdd()));
}
long timeStart = System.currentTimeMillis();
for(Thread t: threads) {
t.start();
}
for(Thread t: threads) {
t.join();
}
System.out.println("Time Use : " + (System.currentTimeMillis() - timeStart) + "ms");
}
}
class RunableAdd implements Runnable {
@Override
public void run() {
long i = 1;
while(true) {
i += i;
if (i > 1000 * 1000* 1000) {
return;
}
}
}
}
Golang代码:
package main
import (
"fmt"
"time"
)
func main() {
chanSignal := make(chan bool)
startTime := time.Now()
goroutineNum := 10
for i := 0; i < goroutineNum; i++ {
go func() {
j := 1
for {
j = j + j
if j > 1000*1000*1000 {
chanSignal <- true
return
}
}
}()
}
for i := 0; i < goroutineNum; i++ {
<-chanSignal
}
fmt.Println(time.Since(startTime))
}
结果:
线程/goroutine数量:10
java : Time Use : 4ms
golang : 19.105µs
线程/goroutine数量:100
java : Time Use : 27ms
golang : 180.272µs
线程/goroutine数量:1000
java: Time Use : 512ms
golang : 1.565521ms
事实证明,golang很好,java擅长"i=i+1",
说明是,当goroutine num加10时,时间成本不到10倍。
====================================结束========================================
我今天用java线程测试golang goroutine,我认为golang goroutine应该比java线程更快,但我的测试结果显示java线程获胜。
环境:java 1.8golang 1.6
cpu是4核
$ cat /proc/cpuinfo | grep 'core id'
core id : 0
core id : 0
core id : 1
core id : 1
Java代码:
import java.util.ArrayList;
import java.util.List;
public class Test {
public static void main(String args[]) throws InterruptedException {
List<Thread> threads = new ArrayList<Thread>();
for(int i = 0; i < 100; i ++) {
threads.add(new Thread(new RunableAdd()));
}
long timeStart = System.currentTimeMillis();
for(Thread t: threads) {
t.start();
}
for(Thread t: threads) {
t.join();
}
System.out.println("Time Use : " + (System.currentTimeMillis() - timeStart) + "ms");
}
}
class RunableAdd implements Runnable {
@Override
public void run() {
int i = 0;
while(true) {
i ++;
if (i == 10 * 1000* 1000) {
return;
}
}
}
}
戈兰代码:
package main
import (
"fmt"
"time"
)
func main() {
chanSignal := make(chan bool)
startTime := time.Now()
goroutineNum := 100
for i := 0; i < goroutineNum; i++ {
go func() {
j := 0
for {
if j == 10*1000*1000 {
chanSignal <- true
return
} else {
j = j + 1
}
}
}()
}
for i := 0; i < goroutineNum; i++ {
<-chanSignal
}
fmt.Println(time.Since(startTime))
}
结果是:
当我设置线程编号10:时
Java : Time Use : 18ms
golang : 50.952259ms
线程编号100:
Java : Time Use : 88ms
golang : Time Use : 458.239685ms
线程编号1000:
Java : Time Use : 1452ms
golang : 4.701811465s
Java线程真的比golang goroutine快吗?或者我的程序出错了?
基准很难正确,当你在衡量其他东西时,很容易认为你在衡量一件事(goroutines/threads的成本)。我想这就是这里正在发生的事情。
例如,线程/goroutines中的内部循环在这两个程序中并不相同。重写goroutine使其更像java代码:
for j := 0; j != 10*1000*1000; j++ {}
chanSignal <- true
结果是速度提高了2倍。
当我在机器上运行它时,这仍然留下了显著的性能差距——go代码在1000个goroutine中需要663毫秒,而java代码在1000线程中需要55毫秒。可能发生的情况是,JVM在线程的run
方法中执行了几次之后,正在JITting掉无用的循环。
这里有一些证据表明goroutines的成本并不相关。在我的机器上,仅仅串行执行代码就可以获得2.55秒的运行时间:
package main
import (
"fmt"
"time"
)
func main() {
goroutineNum := 1000
startTime := time.Now()
for i := 0; i < goroutineNum; i++ {
for j := 0; j < 1000*1000*10; j++ {
}
}
fmt.Println(time.Since(startTime))
}
该代码的基于goroutine的版本在4个处理器上运行时间为663ms,仅略高于串行代码(在我的机器上)所需2.55秒的四分之一。因此,这确实是非常有效的并行,goroutines的成本可以忽略不计。
因此,我认为您主要是在测试java和go执行空循环的效率。看起来这是JIT的一个完美用例,您可以从Java代码出色的运行时性能中看到这一点。
i=i+i测试很愚蠢,因为它几乎只执行24次加法和24<比较,我相信我们都承认,在现实世界中,我们并不经常遇到只用48条机器指令就能完成的问题。
Executors.newFixedThreadPool与Thread相比,更接近于goroutine的Java等价物,请尝试预热,您可能会得到不同的结果。