簡體   English   中英

並行流() java 1.8 與 11

[英]parallelStream() java 1.8 vs 11

考慮以下代碼:

public class StreamDemo {
    public static void main(String[] args) {
        StreamObject obj = new StreamObject();
        obj.setName("mystream");

        List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));

        list.parallelStream().forEach(l -> {
            obj.setId(l);
            System.out.println(obj + Thread.currentThread().getName());
        });
    }

    static public class StreamObject {
        private String name;
        private Integer id;

        // getters, setters, toString()
    }
}

當它使用 java 11 編譯和運行時,它返回以下內容:

StreamObject{name='mystream', id=4}ForkJoinPool.commonPool-worker-23
StreamObject{name='mystream', id=4}main
StreamObject{name='mystream', id=4}ForkJoinPool.commonPool-worker-9
StreamObject{name='mystream', id=4}ForkJoinPool.commonPool-worker-5
StreamObject{name='mystream', id=4}ForkJoinPool.commonPool-worker-19

但是對於 java 1.8,它返回不同的結果:

StreamObject{name='mystream', id=3}main
StreamObject{name='mystream', id=5}ForkJoinPool.commonPool-worker-2
StreamObject{name='mystream', id=2}ForkJoinPool.commonPool-worker-9
StreamObject{name='mystream', id=1}ForkJoinPool.commonPool-worker-11
StreamObject{name='mystream', id=4}ForkJoinPool.commonPool-worker-4

為什么結果不一樣?

這兩個結果都與 Java Memory Model 一致。

執行發生的一種可能順序是:

T1 calls setId
T1 prints
T2 calls setId
T2 prints
...
T5 calls setId
T5 prints

但是,因為您沒有做任何事情來確保 set 和 print 原子發生,所以也允許以下操作(與許多其他排序一樣):

T3 calls setId
T1 calls setId
T2 calls setId
T5 calls setId
T4 calls setId

T1 prints
T1 prints
...
T5 prints

因此,它們不同的原因是規范不要求它們相同。 一些微妙的(或者可能不那么微妙的)實現(或環境)差異意味着它們的執行方式不同。

但是,你說,實現上有什么區別? 這不是你應該關心的事情(這聽起來像是因為不知道而大肆宣傳:我真的不知道)。 您應該關心 Java Memory Model ,因為這提供了保證的屬性。

例如,如果您想要“Java 8”行為,您可以在一個公共監視器上同步線程,例如obj

list.parallelStream().forEach(l -> {
    synchronized (obj) {
        obj.setId(l);
        System.out.println(obj + Thread.currentThread().getName());
    }
});

當然,線程仍然會以任意順序執行; 但是每個線程都會打印它設置的值。

Java 8 和 Java 11 沒有區別,每次運行我們得到不同的結果。 如果我們想正確打印,我們可以使用synchronize塊,但在這種情況下我們將失去parallelStream的好處。

JAVA 8

在此處輸入圖像描述

在此處輸入圖像描述

JAVA 11

在此處輸入圖像描述

在此處輸入圖像描述

請注意,根據 javadoc,此行為是明確的非確定性的,因此兩個輸出都是有效的執行順序。

此操作的行為是明確的非確定性的。 對於並行 stream 管道,此操作不保證遵守 stream 的遇到順序,因為這樣做會犧牲並行性的好處。 對於任何給定的元素,可以在庫選擇的任何時間和任何線程中執行操作。 如果操作訪問共享 state,它負責提供所需的同步。

我想如果您沒有使用 forEachOrdered 方法,而是在 stream 上使用 forEach,這意味着無論您使用哪個 JDK,每次都應該收到不同的值。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM