2013-05-20 17 views
6

Conosco la differenza tra StringBuffer e StringBuilder. read here!Immaginate uno scenario concomitante reale in cui deve essere usato StringBuffer rispetto a StringBuilder?

E, in generale, come il javadoc dice,

Ove possibile, si consiglia di questa classe essere usato di preferenza per StringBuffer in quanto sarà più veloce nella maggior parte delle implementazioni.

Ma, javadoc di StringBuilder dice anche:

istanze di StringBuilder non sono sicuri per l'uso da più thread. Se è necessaria come la sincronizzazione, allora è consigliabile che {@link java.lang.StringBuffer} essere utilizzato

Quindi, mi chiedo, è il caso che StringBuffer è preferito veramente esistito? E poiché la stringa mutabile viene usata principalmente in un singolo thread, qualcuno può darmi uno scenario concomitante del mondo reale che StringBuffer è preferito?

+2

Secondo Joshua Bloch, "thread-safe" non è un tutto o niente, si assolute/nessun fenomeno. Elenca quattro categorie separate di sicurezza del thread che si distinguono principalmente per il grado in cui i client dell'API devono gestire o sincronizzare in modo proattivo. Questo è stato sviluppato da Peter Lawrey anche nella sua risposta. Nessuna classe è un'isola. – scottb

risposta

5

Il motivo per cui StringBuffer è thread-safe è che nel giorno in cui è stata progettata la prima versione di java api, le persone si avvicinavano alla concorrenza in modo diverso rispetto ai giorni nostri. L'opinione prevalente era che gli oggetti dovrebbero essere thread-safe - perché Java supporta i thread, e le persone potrebbero usare qualsiasi classe JDK in più thread. Successivamente, quando Java stava iniziando a essere ottimizzato per il tempo di esecuzione, il costo di quegli inutili blocchi di sincronizzazione iniziò a diventare un problema, quindi le nuove API furono progettate per non essere sincronizzate. Ancora più tardi, la JVM ha iniziato a ottimizzare i blocchi al punto che i blocchi non contestati sono diventati essenzialmente gratuiti, rendendo l'intera decisione un punto controverso.

StringBuffer è ancora thread-safe, perché il vecchio codice potrebbe fare affidamento sul fatto che sia thread-safe. Questo è lontano dall'uso tipico, ma concepibile.

Ad esempio, si supponga di scrivere un file di registro che inoltra voci di registro a un server centrale. Dal momento che non vogliamo bloccare il chiamante mentre attendiamo l'I/O di rete, lo facciamo in un thread dedicato. Le altre discussioni avrebbero accumulano le loro voci di registro in uno StringBuffer:

class RemoteLogger implements Runnable, Appender { 
    final StringBuffer buffer = new StringBuffer(); 

    void append(String s) { 
     buffer.append(s); 
    } 

    public void run() { 
     for (;;) { 
      Thread.sleep(100); 

      String message = buffer.toString(); 
      sendToServer(message); 
      buffer.delete(0, message.length()); 
     } 
    } 
} 
3

Davvero ovunque quando è possibile accedere contemporaneamente a un buffer di testo.

Ad esempio, come fare per avere più thread di scrittore che emetteranno i dati attraverso una rete. In tal caso forse condividono un buffer di testo comune e scrivono direttamente su di esso e quando il buffer è pieno può essere inviato attraverso la rete.

+0

Bel suggerimento, ma scommetto che non puoi scriverlo in un modo sicuro senza dover utilizzare sincronizzato. Avrei usato personalmente BufferedOutputStream. ;) –

+1

Fortunatamente per me lui non voleva che un'implementazione andasse d'accordo con il caso d'uso;) – greedybuddha

+0

Pensando a un modo per scrivere questo senza sincronizzare, ora sono convinto di poterlo fare (grazie per aver reso questa mattina un pensiero esercizio), a patto che tu mi dia la possibilità di sapere alla fine che un buffer non verrà più scritto. Quindi l'idea è solo quella di usare un getter per ottenere il buffer comune che può essere condiviso e scritto. Se il getter vede che la lunghezza del buffer è maggiore di qualche k, pone il buffer su una coda da scrivere e restituisce un nuovo buffer. supponendo che a un certo punto si sappia che il primo buffer in coda non viene utilizzato, si è dorati – greedybuddha

4

La semplice risposta è NO. IMHO non esiste una situazione sana in cui si utilizzi StringBuffer anziché StringBuilder o un'altra classe. Rendere sicuro il thread StringBuffer può rendere il tuo codice meno thread safe perché la gente ritiene erroneamente che se hai usato StringBuffer il tuo codice è thread-safe quando non è così.

Se avete utilizzato StringBuffer, ad un certo punto è necessario utilizzare sincronizzati, anche se dove non è sempre chiaro alla maggior parte degli sviluppatori, e ho visto molti bug in cui questo (anche nelle biblioteche maturi) non è stato fatto o wasn' t fatto correttamente. È molto meglio usare StringBuilder e fare il blocco enternalmente, in modo coerente.

Why a synchronized StringBuffer was never a good idea.

è il caso che StringBuffer è preferito realmente esistito

C'è un caso d'uso; hai una libreria che accetta solo StringBuffer nell'API. Questo è un design scadente per la ragione sopra menzionata, ma la libreria non è perfetta. ;)

+0

Sfortunatamente Java stesso ha almeno una classe che accetta solo StringBuffer (non StringBuilder), ad es. [Matcher.appendReplacement (StringBuffer, String)] (http://docs.oracle.com/javase/6/docs/api/java/util/regex/Matcher.html). –

+0

Mentre sono d'accordo con il sentimento generale, penso che sei un po 'troppo assoluto nel dire che la sincronizzazione esterna è * sempre * richiesta. Almeno, non vedo come il codice che ho inserito nella mia risposta richieda una sincronizzazione aggiuntiva? – meriton

+0

@meriton Unirsi a tutti i thread è effettivamente la sincronizzazione di tutti i thread da interrompere. Puoi dare un esempio in cui aggiungi più di una cosa nel thread in modo sano perché questo non è ovvio per molti sviluppatori? –

2

Il seguente programma a volte genera eccezioni quando si utilizza StringBuilder, ma non genererà mai un'eccezione quando si utilizza StringBuffer.

Programma:

public class StringBuilderConcurrent { 
    static final StringBuilder sb = new StringBuilder(); // shared memory 

    public static void main(String[] args) throws Exception { 
     int NUM_WRITERS = 300; 
     ArrayList<WriterThread> threads = new ArrayList<WriterThread>(NUM_WRITERS); 
     for (int i = 0; i < NUM_WRITERS; i++) { 
      WriterThread wt = new WriterThread("writerThread" + i); 
      threads.add(wt); 
      wt.start(); 
     } 
     for (int i = 0; i < threads.size(); i++) { 
      threads.get(i).join(); 
     }  
     System.out.println(sb); 
    } 

    public static class WriterThread extends Thread { 
     public WriterThread(String name) { 
      super(name); 
     } 
     public void run() { 
      String nameNl = this.getName() + "\n"; 
      for (int i = 1; i < 20; i++) { 
       sb.append(nameNl); 
      } 
     } 
    }; 
} 

Perché StringBuilder (sb) non è thread-safe, avendo più thread scrivono i dati a sb può provocare sb diventare corrotti (ad esempio caratteri nulli inaspettate, le lettere di una parola inframmezzato da qualche altra parola di lettere).È anche possibile che lo stato interno sb s' diventare abbastanza incoerente che un'eccezione può essere generata:

Exception in thread "writerThread0" java.lang.ArrayIndexOutOfBoundsException 
    at java.lang.System.arraycopy(Native Method) 
    at java.lang.String.getChars(String.java:854) 
    at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:391) 
    at java.lang.StringBuilder.append(StringBuilder.java:119) 
    at test.StringBuilderConcurrent$WriterThread.run(StringBuilderConcurrent.java:35) 

Il seguente programma è identico al primo tranne che utilizza StringBuffer invece di StringBuilder. Non incontrerà mai ArrayIndexOutOfBoundsException.

public class StringBufferConcurrent { 
    static final StringBuffer sb = new StringBuffer(); // shared memory 

    public static void main(String[] args) throws Exception { 
     int NUM_WRITERS = 300; 
     ArrayList<WriterThread> threads = new ArrayList<WriterThread>(NUM_WRITERS); 
     for (int i = 0; i < NUM_WRITERS; i++) { 
      WriterThread wt = new WriterThread("writerThread" + i); 
      threads.add(wt); 
      wt.start(); 
     } 
     for (int i = 0; i < threads.size(); i++) { 
      threads.get(i).join(); 
     } 

     System.out.println(sb); 
    } 

    public static class WriterThread extends Thread { 
     public WriterThread(String name) { 
      super(name); 
     } 
     public void run() { 
      String nameNl = this.getName() + "\n"; 
      for (int i = 1; i < 20; i++) { 
       sb.append(nameNl); 
      } 
     } 
    }; 
} 

Se questi programmi sono rappresentativi di un problema del "mondo reale", è una questione abbastanza soggettiva. Lascerò quel giudizio al pubblico.

Problemi correlati