3

次のように、1 つのスレッドでループを反復処理しようとしています。

for (UnitTask task : chain) {
    g.drawLine((int) task.getLocation().getX(), (int) task.getLocation().getY(), (int) currentPos.getX(), (int) currentPos.getY());
    g.fillOval((int) task.getLocation().getX() - 2, (int) task.getLocation().getY() - 2, 5, 5);
    currentPos = task.getLocation();
}

ただし、このオブジェクトに追加できる別のスレッド (Swing イベント スレッド) があります。したがって、ConcurrentModificationException. コードを で囲んでロックを取得しようとしましたsynchronized (chain) { ... }が、それでもエラーが発生します。

Java同期の初心者として、私はその理由について少し混乱しています。これにより、ループがスレッドセーフになると期待していますが、明らかにそうではありません。

興味深いことに、chainはカスタム クラスのインスタンスですが、LinkedList. リスト自体はプライベートであり、外部クラスがそれを直接取得する方法はありません (オブジェクトを明示的に追加/削除するメソッドがあります) ため、これが結果に影響を与えるとは思いません。

4

2 に答える 2

11

の意味

synchronized (c) {
    ... code that uses c ...
}

  • cロックが解除されるのを待ちます
  • ロックc
  • 体を実行する
  • ロックを解除c

したがって、スレッドで同期すると、スレッドはcロックが解除されるのを待ってから飛び込みます。

ここで、 を変更する他のスレッドでコードを同期しない場合、そのコードはロックを待たずに変更を続行します。あるスレッドでブロックを同期しても、別のスレッドがロックを待機することはありません。他のスレッドに次のような行がある場合cc

c.add(someOtherTask)

それが同期ブロックにない場合は、何があっても追加を行います。これが例外の原因です。コードを同期ブロック内のスレッドに配置したにもかかわらず、例外が発生したのもこれが理由です。コードは「規則に従っている」のに、他のスレッドはそれほど気にすることはできませんでした。

ただし、実行時間の長いコードの同期には注意してください。Stephen C が言うように、並行コレクション型を使用する方がよいでしょう。

于 2012-09-16T06:12:01.843 に答える
4

同期は必ずしも役立つとは限りません。

基本的に問題は、反復の進行中にコレクションを変更できないコレクションタイプを使用していることです(イテレータのremoveメソッドを介した場合を除く...サポートされている場合)。これ自体は、スレッド化/同期化の問題ではありません。(そして、単に同期によってそれを解決しようとすると、別の問題が発生する可能性があります。)

反復と変更を同時に実行できるようにする場合は、の代わりにConcurrentLinkedDequeLinkedListなどの別のコレクションタイプを使用する必要があります。


反復と書き込みが別々のスレッドで行われている場合、反復が終了するまで同期して書き込みをブロックするべきではありませんか?それとも私は何かが足りないのですか?

問題は、同期をどのように実装したかにあります。

  • LinkedListバージョンで何らかの同期を明示的に行っていない場合、同期は行われません。

  • いずれかのメソッドによって作成された同期ラッパーを使用する場合Collections.synchronizedXxx、それらのメソッドのjavadocsにはIterator、ラッパーのメソッドによって返されるオブジェクトがiterator()同期されていないことが明確に示されています。

  • 手動で同期を行っている場合は、すべてが同じミューテックスで同期していることを確認する必要があります。そして、そのロックは、反復の期間中、そのミューテックスで保持する必要があります...呼び出しだけではありませんiterator()

また、ロックを長時間保持している場合(たとえば、長いリストを繰り返し処理している場合)、リストを長時間更新する必要がある他のスレッドをブロックする可能性があることに注意してください。この種の問題は、(最悪の場合)システムのパフォーマンスを単一のプロセッサの速度まで低下させる可能性のある同時実行のボトルネックになる可能性があります。

ConcurrentXxxクラスは通常、イテレータによって生成されたシーケンスの一貫性の保証を緩和することにより、これを回避します。たとえば、反復を開始した後にコレクションに追加された要素が表示されない場合があります。

于 2012-09-16T05:33:43.983 に答える