39

Słyszałem, że Java 8 oferuje wiele narzędzi dotyczących przetwarzania współbieżnego. Dlatego zastanawiam się, jaki jest najprostszy sposób na paralelizację danej pętli for?Java 8: Równoległa pętla FOR

public static void main(String[] args) 
{ 
    Set<Server> servers = getServers(); 
    Map<String, String> serverData = new ConcurrentHashMap<>(); 

    for (Server server : servers) 
    { 
     String serverId = server.getIdentifier(); 
     String data = server.fetchData(); 

     serverData.put(serverId, data); 
    } 
} 

Odpowiedz

44

Przeczytać na streams, wszystkie są nowe wściekłość.

płatna szczególnie baczną uwagę na nieco o równoległości:

„elementów przetwarzaniem z wyraźnym dla pętli jest z natury seryjny Strumienie ułatwić wykonanie równolegle przez przewartościowania obliczeń jako rurociągu operacji zbiorczych, zamiast. jako konieczne operacje na każdym elemencie. Wszystkie operacje strumieniowe mogą być wykonywane szeregowo lub równolegle. "

Podsumowując, nie ma równoległych pętli for-one, są one z natury szeregowe. Strumienie mogą jednak wykonać to zadanie. Spójrz na poniższy kod:

Set<Server> servers = getServers(); 
    Map<String, String> serverData = new ConcurrentHashMap<>(); 

    servers.parallelStream().forEach((server) -> { 
     serverData.put(server.getIdentifier(), server.fetchData()); 
    }); 
+1

To jest niesamowite. Dziękuję –

+0

Eh, podałem dokładnie tę samą odpowiedź ... – fge

+4

Należy zauważyć, że strumienie równoległe mają narzut: nie zawsze poprawia wydajność w stosunku do strumienia szeregowego (lub normalnie dla każdego). –

13

To byłoby za pomocą Stream:

servers.parallelStream().forEach(server -> { 
    serverData.put(server.getIdentifier(), server.fetchData()); 
}); 

I podejrzewasz Collector mogą być wykorzystane do większego efektu tutaj, ponieważ użyć współbieżne kolekcję.

+0

Cóż, po doradztwo, teraz można przeczytać wyrażenia lambda a także znać pewne szczegóły strumieni;) – dosdebug

4

bardziej elegancki i funkcjonalny rozwiązaniem będzie po prostu za pomocą kolekcjonerów toMap lub funkcję toConcurrentMap, które unikają utrzymaniu inną stanową zmienną dla ConcurrentHashMap, jak poniższy przykład:

final Set<Server> servers = getServers(); 
Map<String, String> serverData = servers.parallelStream().collect(
    toConcurrentMap(Server::getIdentifier, Server::fetchData)); 

Uwaga: 1. Te interfejsy funkcjonalne (Server::getIdentifier or Server::fetchData) nie zezwalają na wyjątek sprawdzany przez wyrzucanie tutaj, 2. Aby uzyskać pełne korzyści ze strumienia równoległego, liczba serwerów byłaby duża i nie ma w nich zaangażowanych operacji wejścia/wyjścia, czysto danych przetwarzanie w tych funkcjach (getIdentifier, fetchData)

Proszę odnieść się do kolekcjonerów javadoc w http://docs.oracle.com/javase/8/docs/api/java/util/stream/Collectors.html#toConcurrentMap

-3

używając mojego Parallel.For, kod może wyglądać następująco,

public staic void main(String[] args) 
{ 
    Set<Server> servers = getServers(); 
    Map<String, String> serverData = new ConcurrentHashMap<>(); 

    Parallel.ForEach(servers, new LoopBody<Server>() 
    { 
     public void run(Server server) 
     { 
      String serverId = server.getIdentifier(); 
      String data = server.fetchData(); 

      serverData.put(serverId, data); 
     } 
    }); 
}