2017-05-17 73 views
3

我想計算a^b,例如, 2^30,當計算a^b爲什麼並行不行,但parallelStream可能

public long pow(final int a, final int b) 

第一我用這種方式

return LongStream.range(0, b).reduce(1, (acc, x) -> a * acc); // 1073741824 

得到正確的結果。然後我想並行計算的話,自然就會去把它改成

return LongStream.range(0, b).parallel().reduce(1, (acc, x) -> a * acc); // 32 

但在這種情況下,結果就是32。爲什麼?因此,對於支持並行

我在這種情況下,它的工作原理再次

return Collections.nCopies(b,a).parallelStream().reduce(1, (acc, x) -> acc * x); // 1073741824 

改變了它。

那麼parallel的方式有什麼問題?

回答

5

reduce要求提供的功能爲associative。您的功能(acc, x) -> a * acc不符合要求,從而違反合同。

要關聯,函數必須滿足(x op y) op z == x op (y op z)對於任何x,y和z。但是對於你的功能,(x op y) op z = x*a^2x op (y op z) = x * a

此外,提供給reduce的第一個參數必須是關於累加器函數的標識。所以對任何x來說1 op x == x肯定是真的。但從1 op x == a開始,這也不適用於您的累加器功能。

正確的方式做,這就是:

LongStream.range(0, b).map(x -> a).reduce(1, (u, v) -> u * v); 

這是保證流是並行或串行的正常工作。

+0

我仍然困惑,如果它不聯想爲什麼第一種方式 - 沒有平行 - 它的作品?我甚至試圖模擬這個結果--32,我不能再現它。我的代碼是https://gist.github.com/zhugw/248f2942ce955877a18a2f3f4fa4c68e我認爲這種方式是通過並行實現的。 – zhuguowei

+1

它恰好工作,因爲當前的順序執行**發生**實際上不取決於2個要求(關聯和身份)中的任何一個。並行實現取決於兩者。但是不能保證你的第一個樣品將繼續工作。 Java標準庫或甚至未來版本的JDK的替代實現可能會以破壞它的方式實現。 – Misha

+0

你好,我有一些新的發現。請參閱下面的答案。 – zhuguowei

1

跟蹤源代碼後,我終於知道爲什麼結果是32

return LongStream.range(0, b).parallel().reduce(1, (acc, x) -> a * acc); // 32 

相關源代碼

// java.util.stream.ReduceOps.ReduceTask#onCompletion 
    @Override 
    public void onCompletion(CountedCompleter<?> caller) { 
     if (!isLeaf()) { 
      S leftResult = leftChild.getLocalResult(); 
      leftResult.combine(rightChild.getLocalResult()); // here to combine 
      setLocalResult(leftResult); 
     } 

    } 

    // java.util.stream.TerminalOp 
    @Override 
    public void combine(ReducingSink other) { 
     accept(other.state); 
    } 
    @Override 
    public void accept(long t) { 
     state = operator.applyAsLong(state, t); // (acc, x) 
    } 

,因爲實際上沒有拉姆達使用x

(acc, x) -> a * acc; 

所以實際效果是這樣的

leftResult.combine(2); 

Online demo來模擬這種現象。

相關問題