2017-07-11 13 views
8

Giả sử tôi có mã này:flatMap song song luôn tuần tự

Collections.singletonList(10) 
      .parallelStream() // .stream() - nothing changes 
      .flatMap(x -> Stream.iterate(0, i -> i + 1) 
        .limit(x) 
        .parallel() 
        .peek(m -> { 
         System.out.println(Thread.currentThread().getName()); 
        })) 
      .collect(Collectors.toSet()); 

Output là tên chủ đề tương tự, vì vậy không có lợi ích từ parallel ở đây - những gì tôi có ý nghĩa bởi đó là có một chủ đề duy nhất mà không Tất cả công việc.

Bên flatMap có mã này:

result.sequential().forEach(downstream); 

Tôi hiểu buộc sequential tài sản nếu "bên ngoài" dòng sẽ là song song (họ có thể có khối), "bên ngoài" sẽ phải đợi cho "flatMap "để hoàn thành và cách khác xung quanh (kể từ khi cùng một hồ bơi chung được sử dụng) Nhưng tại sao luôn luôn lực lượng đó?

Đó có phải là một trong những điều mà có thể thay đổi trong phiên bản mới hơn không?

Trả lời

8

Có hai khía cạnh khác nhau.

Đầu tiên, chỉ có một đường ống đơn lẻ hoặc là tuần tự hoặc song song. Sự lựa chọn của tuần tự hoặc song song ở dòng bên trong là không liên quan. Lưu ý rằng người tiêu dùng downstream bạn thấy trong đoạn mã được trích dẫn đại diện cho toàn bộ luồng tiếp theo, vì vậy trong mã của bạn, kết thúc bằng .collect(Collectors.toSet());, người tiêu dùng này cuối cùng sẽ thêm các phần tử kết quả vào một cá thể Set đơn lẻ không an toàn. Vì vậy, xử lý dòng bên trong song song với người tiêu dùng duy nhất đó sẽ phá vỡ toàn bộ hoạt động.

Nếu luồng bên ngoài bị tách, mã được trích dẫn có thể được gọi đồng thời với những người tiêu dùng khác nhau thêm vào các nhóm khác nhau. Mỗi cuộc gọi này sẽ xử lý một phần tử khác nhau của ánh xạ luồng ngoài tới một thể hiện dòng bên trong khác nhau. Vì luồng bên ngoài của bạn chỉ bao gồm một phần tử duy nhất nên không thể tách rời.

Cách này đã được triển khai, cũng là lý do cho vấn đề Why filter() after flatMap() is “not completely” lazy in Java streams?, vì forEach được gọi trên luồng bên trong sẽ chuyển tất cả các yếu tố cho người tiêu dùng ở hạ lưu. Như được minh họa bởi this answer, có thể thực hiện thay thế, hỗ trợ sự lười biếng và tách phân luồng. Nhưng đây là một cách cơ bản khác nhau để thực hiện nó. Thiết kế hiện tại của việc triển khai luồng chủ yếu hoạt động theo thành phần người tiêu dùng, do đó, cuối cùng, trình tách nguồn (và các phân tách khỏi nó) nhận được một số Consumer đại diện cho toàn bộ đường ống trong một số tryAdvance hoặc forEachRemaining. Ngược lại, giải pháp của câu trả lời được liên kết làm thành phần bộ tách, tạo ra một số Spliterator mới ủy quyền cho bộ tách nguồn. Tôi cho rằng, cả hai cách tiếp cận đều có lợi thế và tôi không chắc chắn, việc triển khai OpenJDK sẽ mất bao nhiêu khi làm việc theo cách khác.

+0

Xin chào, thưa ngài. có phải là một lỗi luồng không? –

+1

@ holi-java Tôi sẽ không nói rằng đây là lỗi, chỉ thiết kế triển khai kém mà rất có thể sẽ được khắc phục trong tương lai. –

+5

@ holi-java: sự lười biếng mất tích có thể được xem như là một lỗi và đã có một báo cáo lỗi cho nó. Tuy nhiên, giới hạn song song chỉ là một khu vực để cải thiện hiệu suất tiềm năng. Trong thực tế, điều này chỉ ảnh hưởng đến các luồng với một số lượng nhỏ các phần tử trong luồng ngoài và các luồng bên trong lớn hơn nhiều. – Holger

Các vấn đề liên quan