2015-10-29 4 views
9

ここでは、リストにアクセスできる2つのスレッドを持つクラスがあります。 1つのスレッドは定期的に更新されたコピーでリストを置き換え、もう1つのスレッドはリストの内容を画面にペイントします。1つのスレッドでのみ変更されたリストへのアクセスを同期する必要はありますか?

public class ThreadSafePainter { 
    private List<String> dataList = new ArrayList<>(); 

    /* 
    * starts a thread to periodically update the dataList 
    */ 
    public ThreadSafePainter() { 
     Thread thread = new Thread(() -> { 
      while (true) { 
       // replace out-dated list with the updated data 
       this.dataList = getUpdatedData(); 
       // wait a few seconds before updating again 
       Thread.sleep(5000); 
      } 
     }); 
     thread.start(); 
    } 

    /* 
    * called 10 times/second from a separate paint thread 
    * Q: Does access to dataList need to be synchronized? 
    */ 
    public void onPaint(Graphics2D g) { 
     Point p = new Point(20, 20); 

     // iterate through the data and display it on-screen 
     for (String data : dataList) { 
      g.drawString(data, p.x, p.y); 
      p.translate(0, 20); 
     } 
    } 

    /* 
    * time consuming data retrieval 
    */ 
    private List<String> getUpdatedData() { 
     List<String> data = new ArrayList<>(); 
     // retrieve external data and populate list 
     return data; 
    } 
} 

私の質問は、データリストへのアクセスを同期する必要がありますか?それをどうやってやりますか?

public ThreadSafePainter() { 
    ... 
      synchronized (this) { 
       this.dataList = getUpdatedData(); 
      } 
    ... 
} 

public void onPaint(Graphics2D g) { 
    ... 
    synchronized (this) { 
     for (String data : dataList) 
      ... 
    } 
} 
+0

すべての繰り返しで画面全体を再描画しますか? – StackFlowed

+5

'getUpdatedData()'は毎回新しいリストを作成するので、安全なパブリケーションのみが必要です。この場合、 'dataList'フィールドを' volatile'と宣言するだけで十分です。リストの参照が格納された後にリストの参照が格納され、再度変更されない場合(次の更新が新しいリストを作成するとき)、読み込み者が処理ごとに参照を読み込むことが重要です( 'for(...:dataList) ')。 1つの 'paint'の間にリストに複数回アクセスする必要がある場合は、それをローカル変数に格納する必要があります。 – Holger

+4

2つ以上のスレッドが**変更可能な**状態を共有するたびに、**同時実行性を処理するための何らかのメカニズムが必要です**。低レベルの同期であれ、より高いレベルの並行処理クラスであれ、 'Atomic *'クラスまたは 'volatile'フィールドは実際の状況に依存しますが、何かを常に配置しなければなりません。 – biziclop

答えて

-1

公式のJavaドキュメントは、ArrayListが同期していないことを指摘しています。だからあなたはそれを同期する必要があります。

しかし、ドキュメントでは、複数のスレッドが同じリストにアクセスする場合にのみ、これが適用されることも記載しています。あなたのケースでは、それを同期する必要はありません。しかし、あなたはこの単純な呼び出しであなたのリストを同期させることができ、100%を確認したい場合:「data_typeが」保存したいタイプの値である

List<data_type> list = Collections.synchronizedList(new ArrayList<data_type>()); 

...。

+5

その文書は誤解を招くものです。リストを同期する必要がない場合でも、リストへの参照を安全に公開する必要があります。 – biziclop

0

リストを同期させない場合は、リストを揮発性にする必要があります。このようにして、読み取りスレッドはList変数の値の最新コピーを取得します。

Hereは良い説明です。

1

同じ可変状態に複数のスレッドがアクセスしている場合(ほとんどの場合、他のスレッドの有効期間内に状態が変化しないことがわかっている場合など、いくつかの例外があります)、いくつかアクションの種類。この場合、フィールドdataListを突然変異させており、別のスレッドがこれに反応することを期待しています。だから、あなたは「何か」をする必要があります。最も一般的な解決方法は​​です。これを行う方法の概要は問題ありません。

GUIの問題ではおかしなことですが、何かの最大限のパフォーマンスを使いたい場合や、同時実行性を十分に理解したい場合は、より限定された状況。この場合、ライターは1人しかなく、ライターは1つの参照のみを作成しています。このような場合は、volatileで十分です。この種のコードでは、おそらく別のライタースレッドなどを追加するなど、コードを変更したときに壊れにくいため、個人的には​​に固執します。

関連する問題