6

私はStringBufferとStringBuilderの違いを知っています。 read hereStringBuilderをStringBuilderよりも使用する必要がある実際の同時シナリオを想像してみてください。

そして、javadocが言うように、一般的に、

可能

は、より速くほとんどの実装の下になるように、このクラスは、StringBufferに、優先して使用することをお勧めします。

しかし、StringBuilderののjavadocのも書かれています:StringBuilder

インスタンスが複数のスレッドで安全に使用できません。このような同期が必要な場合、それは{@link java.lang.StringBufferは}そう

を使用することをお勧めします、私は疑問に思って、StringBufferのが本当に存在していたことが好ましい場合ですか?そして、変更可能な文字列はほとんどの場合単一のスレッドで使用されるため、StringBufferが優先される現実的なシナリオを同時に提供することはできますか?

+2

ジョシュアブロッホによれば、「スレッドセーフ」は絶対的なイエス/ノー現象ではありません。彼は、APIのクライアントが積極的に管理または同期する必要がある程度によって主に区別されるスレッド安全性の4つのカテゴリを列挙しています。これは彼の反応でもPeter Lawreyによって開発されたものです。クラスは島ではありません。 – scottb

答えて

5

StringBufferがスレッドセーフである理由は、Java APIの最初のバージョンが設計された日に、人々が今日よりも並行性に違って近づいたためです。 Javaではスレッドがサポートされており、複数のスレッドでJDKクラスを使用する可能性があるため、オブジェクトはスレッドセーフである必要があります。その後、Javaが実行時間に最適化され始めたとき、不要な同期ブロックのコストが問題になり始めたため、新しいAPIは同期されないように設計されました。それでもなお、JVMは、競合のないロックが本質的にフリーになった時点でロックを最適化し、決定全体を議論の対象にしました。

StringBufferはスレッドセーフであり、古いコードはスレッドセーフである可能性があるためです。それは典型的な使用にはほど遠いが、考えられる。

たとえば、ログエントリを中央サーバーに転送するログファイルアペンダを作成しているとします。ネットワークI/Oを待つ間に呼び出し元をブロックしたくないので、それを専用のスレッドで行います。他のスレッドは、それらのログエントリを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

テキストバッファが同時にアクセスされる可能性があります。

例として、ネットワークを介してデータを出力する複数のライタースレッドを持つ方法について説明します。その場合は、共通のテキストバッファを共有し、直接書き込むだけで、バッファがいっぱいになるとネットワーク経由で送信される可能性があります。

+0

素晴らしい提案ですが、同期を使用することなくスレッドセーフな方法で書くことはできません。私はBufferedOutputStreamを自分で使っていただろう。 ;) –

+1

私にとって幸運なことに、彼はインプリメンテーションをユースケースに合わせることを望んでいませんでした;) – greedybuddha

+0

これを同期させずに書く方法を考えて、今私はそれを行うことができると確信していますあなたが私に最終的にバッファがもう書き込まれないことを知る能力を与えている限り)。だから、アイデアはゲッターを使って共通のバッファを取得して共有したり書き込んだりするだけです。 getterがバッファの長さがいくつかのkより大きいと判断すると、バッファは書き込まれるキューに置き換えられ、新しいバッファが返されます。ある時点でキューの最初のバッファが使用されていないことがわかっている場合、あなたはゴールデンです – greedybuddha

4

簡単な答えはNOです。 IMHO StringBuilderや別のクラスの代わりにStringBufferを使用するのは難しい状況はありません。人々は誤ってあなたが使用している場合はStringBufferのあなたのコードは、これがそうでないときにスレッドセーフであると仮定ので、作るのStringBufferスレッドセーフは、あなたのコードより少ないスレッドセーフにすることができます。

StringBufferを使用している場合は、ほとんどの開発者にとって必ずしも明確ではないものの、ある時点ではsynchronizedを使用する必要があります。また、これ(成熟したライブラリでさえ)が行われなかったり、正しく行われました。 StringBuilderを使用し、永続的に一貫してロックを行うほうがはるかに優れています。

Why a synchronized StringBuffer was never a good idea.

のStringBufferが好まれる場合が本当に

1のユースケースがありますが存在しています。 APIにStringBufferだけを受け入れるライブラリがあります。これは上記の理由でデザインが貧弱ですが、ライブラリは完璧ではありません。 ;)

+0

残念ながら、Java自体にはStringBuffer(StringBuilderではない)を受け入れる少なくとも1つのクラスがあります。 [Matcher.appendReplacement(StringBuffer、String)](http://docs.oracle.com/javase/6/docs/api/java/util/regex/Matcher.html)。 –

+0

私は一般的な感情に同意しますが、私は外部同期が常に必要とされていると言ってもあまりにも絶対的だと思います。少なくとも、自分の答えに投稿したコードが追加の同期を必要としているのを見ていませんか? – meriton

+0

@meritonすべてのスレッドに参加すると、すべてのスレッドが実質的に同期して停止します。多くの開発者にとって明らかではないので、スレッドに複数のものを追加するとよいでしょうか? –

2

次のプログラムでは、StringBuilderのを使用している場合、時には例外をスローしますが、StringBufferのを使用しているときに例外をスローすることはありません。

プログラム:

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); 
      } 
     } 
    }; 
} 

のStringBuilder(sb)はスレッドセーフではないので、複数のスレッドがsbにデータを書き込む有する他のいくつかの単語のが散在しsbになってきて壊れた(例えば、予期しないヌル文字、単語の手紙をもたらすことができます手紙)。sbの内部状態が十分に矛盾になることは、例外がスローされる可能性もあります:それは代わりにStringBuilderのStringBufferのを使用しています以外

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) 

次のプログラムは、最初のと同じです。 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); 
      } 
     } 
    }; 
} 

これらのプログラムが「現実世界」の問題であるかどうかは、かなり主観的な質問です。私はその判断を聴衆に任せます。

関連する問題