2016-03-20 12 views
5

Quindi, sto cercando di ottenere la mia testa intorno al API flusso introdotto in Java 8. Sto cercando di fare un corso d'acqua che può essere eseguito su un thread separato (solo per scopi didattici)Stream.parallel() usa una nuova discussione?

String oracle = "http://www.oracle.com"; 
URL url = new URL(oracle); 
BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream())); 
in.lines().parallel().forEach(System.out::println); 
System.out.print("CLOSING THE INPUT STREAM!, shouldnt this crash?"); 
in.close(); 

Il il risultato non è quello che mi aspetterei .. (mi aspettavo un crash, dal momento che ho chiuso il flusso di input mentre l'altro thread sta leggendo da esso). Prendere nota della chiamata al metodo .parallel(). Invece il codice sembra essere eseguito in modo sequenziale senza problemi.

USCITA:

<script language="JavaScript" src="http://www.oracleimg.com/us/assets/metrics/ora_ocom_hp.js"></script> 
<!-- End SiteCatalyst code --> 

      <!-- SS_END_SNIPPET(fragment6,1)--> 
<!-- SS_BEGIN_SNIPPET(fragment7,ui)-->   <!-- SS_END_SNIPPET(fragment7,ui)--> 
</html> 
CLOSING THE INPUT STREAM!, shouldnt this crash? 

Qualcuno sa cosa sta succedendo? Perché il mio codice non si arresta in modo anomalo?

+2

È possibile utilizzare 'ForkJoinPool' se non si desidera che le cose vengano bloccate. – Maroun

risposta

12

Il flusso parallelo tenterà effettivamente di dividere il lavoro di lettura delle linee su thread multipli. Ma l'invocazione stessa sta bloccando, cioè la dichiarazione attende che tutti i thread finiscano per passare alla successiva istruzione (dove si chiude il flusso di input).

Una cosa da notare è che forEach non garantisce che le azioni parallele vengano eseguite nello stesso ordine degli elementi del flusso, pertanto le linee stampate in questo caso potrebbero non essere nello stesso ordine con la pagina Web originale (vedere https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#forEach-java.util.function.Consumer-).

+0

Capisco, buona risposta, qual è il punto di creare una nuova discussione se stai per lasciare l'attuale inattivo? .parallel() doenst sembra essere tutto ciò che è utile. Immagino che sia una domanda diversa. Accetterà la tua domanda come accettata al più presto. Grazie – feresr

+2

@feresr: Il punto è che, se genera (ad esempio) dieci thread per eseguire il lavoro in parallelo, l'intera operazione potrebbe essere dieci volte più veloce di se eseguita in una sola. (In pratica potrebbe non andare così veloce - o potrebbe andare ancora più veloce, a causa degli effetti di memorizzazione nella cache - ma è comunque l'idea!) – psmears

+2

Con stream paralleli, l'esecuzione potrebbe essere più veloce. Puoi provare a misurare la differenza rispetto a un flusso sequenziale. Ha ancora bisogno di bloccare perché 'forEach' è un'operazione _terminale_ che deve combinare un determinato risultato. Puoi anche chiamare un'operazione 'sum' nel qual caso deve aspettare che il risultato sia combinato. – manouti

2

Se si desidera eseguire le cose in background senza bloccare immediatamente il loro completamento, è possibile utilizzare java.util.concurrent.CompletableFuture.runAsync(Runnable) e metodi correlati. Restituisce un CompletableFuture che può essere unito in seguito, se necessario.

1

Come già notato, il flusso parallelo blocca il thread corrente fino a quando tutte le attività parallele sono finite. In effetti, il thread corrente viene solitamente utilizzato anche per eseguire alcuni lavori, ma se termina la sua parte, quindi attende altri thread (o ruba parte del loro lavoro per aiutarli).

C'è un caso particolare: se l'operazione di flusso parallelo genera un'eccezione, l'elaborazione del flusso nel thread principale termina (eccezionalmente), ma altri thread in background potrebbero continuare a elaborare alcuni blocchi di input. È possibile controllare questo utilizzando il seguente codice:

// Create list of Strings "0", "1", "2", ..., "99" 
List<String> list = IntStream.range(0, 100).mapToObj(String::valueOf) 
          .collect(Collectors.toCollection(ArrayList::new)); 
// replace one with non-numeric 
list.set(1, "foo"); 

// Convert every string to number and print it 
try { 
    list.parallelStream().mapToInt(Integer::parseInt).forEach(System.out::println); 
} catch (NumberFormatException e) { 
    // well some non-number encountered 
} 
System.out.println("Exited"); 

L'esecuzione di questo codice può capitare di vedere che alcuni numeri sono stampati dopo il messaggio "Exited".