2015-05-27 8 views
12

Il mio codice:StringBuilder è thread-safe (si usa con parallelStream)?

StringBuilder sb = new StringBuilder(); 

events.parallelStream().forEach(event -> { 
    sb.append(event.toString()); 
    sb.append("\n"); 
}); 

non mi interessa circa l'ordine del events.toString() nel risultato finale. Ma mi interessa che lo events.toString() appaia correttamente una riga dopo l'altra, senza mescolare/incasinare ovviamente.

È parallelStream (anziché stream) sicuro a questo proposito?

+1

Anche se si è utilizzato un StringBuffer, è possibile ottenere due eventi di seguito e due a capo di nuova riga. – Random832

+0

La risposta è no. (potrebbe essere utile: [Come faccio a dimostrare a livello di programmazione che StringBuilder non è protetto da thread?] (https://stackoverflow.com/questions/48558432)) – Andrew

risposta

17

La soluzione migliore è quella di utilizzare

events.parallelStream().map(event -> event+"\n").collect(Collectors.joining()); 

o alternativamente (grazie a @Holger):

events.parallelStream().map(Object::toString).collect(Collectors.joining("\n", "", "\n")); 

In generale evitate utilizzando forEach funzionamento come terminale per i flussi. Di solito le operazioni di riduzione come collect o reduce sono alternative migliori.

+0

La raccolta delle stringhe avverrà dopo tutto il lavoro dei flussi paralleli o nel mezzo di esso? Perché penso che 'parallelStream' usi il framework di fork join dietro le quinte. –

+0

L'attività verrà suddivisa in parti e saranno collegate in modo indipendente in parallelo. Alla fine le parti verranno unite insieme nella stringa risultante. Usa 'StringBuilder' internamente, ma parti diverse usano istanze diverse, quindi non ci sono problemi con la sincronizzazione qui. –

+6

Ancora meglio: 'events.parallelStream(). Map (Object :: toString) .collect (Collectors.joining (" \ n "," "," \ n "));' – Holger

4

No, non lo è. Come indicato in its javadoc:

Una sequenza di caratteri mutabile. Questa classe fornisce un'API compatibile con StringBuffer, ma senza alcuna garanzia di sincronizzazione.

Utilizzare invece StringBuffer.

+2

Nota che probabilmente non trarrai vantaggio dalla parallelizzazione se usi 'StringBuffer' in questo Astuccio. Sono abbastanza sicuro che sarà ancora più lento del flusso sequenziale poiché in realtà nulla verrà eseguito in parallelo; tutti i thread tranne uno attenderanno solo i monitor. –

+0

@TagirValeev Questo non è vero. I metodi toString degli eventi verranno eseguiti in parallelo. – Random832

8

No, non è thread-safe.

Questa è la differenza principale tra il vecchio StringBuffer e il nuovo StringBuilder - i metodi precedenti sono sincronizzati, mentre quelli di quest'ultimo non lo sono.

Non è molto utile farlo in questo modo, anche se dovresti usare StringBuffer - i thread dovrebbero attendere l'uno sull'altro per scrivere sullo StringBuffer.

+4

E non dimenticare, anche quando si usa 'StringBuffer', le due chiamate' append' fatte all'interno dell'espressione lambda potrebbero essere arbitrariamente intercalate quando l'espressione lambda viene eseguita contemporaneamente senza sincronizzazione aggiuntiva. – Holger