3

Javaにfor-eachループがあります。

for-eachはmyListスレッドで実行されます。myList同時に成長することができます。いくつか質問があります:

  1. for-eachループを開始し、開始後にアイテムがリストに追加された場合、for-eachループのアクションはその上で実行されますか?
  2. 上記の質問に対する答えが「いいえ」であると仮定します。ちょっと問題があります。for-eachループはwhile(true)ループ内にあるため、最初からやり直します。for-eachループを各アイテムで1回実行する必要があります。を取得するため、for-eachループ中にアイテムを削除できませんConcurrentModificationException。したがって、私の解決策は、for-eachループが終了した後にすべてのリストアイテムを削除することです。ただし、このように、for-eachループの開始後にリストに追加されたアイテムも削除された場合、for-eachループはこのアイテムで実行されません。

私の目標は、同時に成長できるリストで実行されるfor-eachを作成することです。for-eachループでアイテムを見逃したり、同じアイテムで2回以上実行したりしないようにします。解決策は何ですか?

4

3 に答える 3

6

Iterator.remove を使用すると、ConcurrentModificationException にヒットしないようにすることができますが、別の解決策は、foreach ループを使用せずに単純にループすることです。

// assuming that this is a list of Strings
List<String> list = ...
while(!list.isEmpty())) {
    String data = list.remove(0);
    ...process data...
}

これにより、リストに追加された各アイテムを一度だけ処理できます。上に小さなウィンドウがありますが、isEmpty が true を返し、新しいアイテムがリストに追加される可能性があります (これはマルチスレッド環境で発生する可能性があります)。

于 2013-02-15T12:14:33.760 に答える
4

これは典型的な生産者と消費者の問題です。リストまたはリストの実装を使用することは想定されていません。List はインデックス ベースであるため、リストへのアイテムの追加/削除により、他の要素のインデックスが変更されます。

キューの実装を使用しようとしています。

あなたの場合、他のスレッド (プロデューサー) はキューにエンキューし、foreach ブロックを実行するコード/スレッド (コンシューマー) のブロックは、キューからデキューして処理を行う必要があります。

それが目的にかなうかどうか教えてください。あなたのユースケースについての私の理解が間違っている場合は、明確にしてください。

--

ヴィノード

于 2013-02-15T12:18:36.687 に答える
0

リストの一種のダブルバッファリングを探していると思います。

複数のプロデューサーと複数のコンシューマーでこれをテストしましたが、完全に機能しているようです。

基本的に、要求されたときに新しい空のリストに置き換えられるリストを保持する必要があります。スワッピング中にスレッドを正しく処理すると、少し複雑になります。これは、リストに追加する複数のスレッドと、繰り返しのためにリストを取得する複数のスレッドを処理します。

アーキテクチャのわずかな変更 (リストから一度に 1 つずつエントリをプルする) はBlockingQueue、より良い解決策である を使用できることを意味することに注意してください。

public class DoubleBufferedList<T> {
  // Atomic reference so I can atomically swap it through.
  // Mark = true means I am adding to it so unavailable for iteration.
  private AtomicMarkableReference<List<T>> list = new AtomicMarkableReference<List<T>>(newList(), false);

  // Factory method to create a new list - may be best to abstract this.
  protected List<T> newList() {
    return new ArrayList<T>();
  }

  // Get and replace the current list.
  public List<T> getList() {
    // Atomically grab and replace the list with an empty one.
    List<T> empty = newList();
    List<T> it;
    // Replace an unmarked list with an empty one.
    if (!list.compareAndSet(it = list.getReference(), empty, false, false)) {
      // Failed to replace! 
      // It is probably marked as being appended to but may have been replaced by another thread.
      // Return empty and come back again soon.
      return Collections.EMPTY_LIST;
    }
    // Successfull replaced an unmarked list with an empty list!
    return it;
  }

  // Add an entry to the list.
  public void addToList(T entry) {
    List<T> it;
    // Spin on get and mark.
    while (!list.compareAndSet(it = list.getReference(), it, false, true)) {
      // Spin on mark.
    }
    // Successfully marked! Add my new entry.
    it.add(entry);
    // Unmark it. Should never fail because once marked it will not be replaced.
    if (!list.attemptMark(it, false)) {
      throw new IllegalMonitorStateException("it changed while we were adding to it!");
    }
  }
}
于 2013-02-15T14:43:00.793 に答える