1

これはばかげた質問であることは知っていますが、これを修正する方法がわかりません。これまでスレッドを使用した経験があまりありません。

以下では、最初にコマンド output.write(mylist) を 10 秒ごとに実行するタイマーを作成する必要があります。このタイマーは、単に mylist の内容を出力します。

次に、私が持っている約 3 つのリストをループし、それぞれについて、リスト内の次の単語を取得してループし続けるスレッドを作成します。注意: これは削除されたバージョンであり、完全ではないため、arraylist / list についてコメントせずに、エラー自体についてコメントしてください。

同時変更の例外が頻繁に発生しますが、実行しようとすると常に発生するわけではありませんoutput.write()。これは、他のスレッドの1つが現在何かを保存しているためだとmylist思いますか? これを修正するにはどうすればよいですか?

    Runnable timer = new Runnable() {
        public void run() {
            try {
                while (true) {
                    Thread.sleep(10000);
                    output.write(mylist);
                }
            } catch (InterruptedException iex) {}
        }
    };
    Thread timerThread = new Thread(timer);
    timerThread.start();

    for (final ValueList item : list) {

        Runnable r = new Runnable() {
            public void run() {
                try {
                    while (true) {

                        char curr = item.getNext();

                         mylist.addWord(curr);
                    }
                } catch (InterruptedException iex) {}
            }
        };

        Thread thr = new Thread(r);
        thr.start();
    }
}
4

3 に答える 3

9

頻繁に発生する同時変更例外がありますが、実行しようとすると常にではありませんoutput.write...

問題は、別のスレッドが呼び出しているのと同時に、output.write(...)メソッドがあなたを繰り返し処理していることです。が並行コレクションでない限り、これは許可されません。myListmyList.addWord(curr);myList

これを修正するにはどうすればよいですか?

アクセスするたびに同期する必要がありmyListます。この場合は、出力するときや単語を追加するときです。

  synchronized (myList) {
      output.write(mylist);
  }
  ...

  synchronized (myList) {
      myList.addWord(curr);
 }

この場合、はおそらくリストを反復処理しているため、呼び出し元が反復子を同期する必要があるためoutput.write(mylist)、メソッドを使用できません。Collections.synchronizedList(...)

これが何回も呼び出される高パフォーマンスのメソッドである場合は、 a を使用することもできますがConcurrentLinkedQueue、これは明らかにリストではなくキューです。 ConcurrentSkipList別のオプションですが、それはより重いデータ構造です。

于 2012-10-04T21:25:36.977 に答える
1

あなたが言ったように、これはJavaイテレータがフェイルファストであり、コレクションが同時に変更されると失敗するために発生しています。1 つの簡単な解決策は、リストへのアクセスを で保護することですsynchronizedが、これによりスレッドが停止し、出力が完了するのを待ちます。

おそらくよりスマートな解決策の 1 つは、List の同時実装を使用することです。あなたのリストは頻繁に変更されるため、CopyOnWriteArrayListあなたが望む解決策ではありません. あまり多くの変更を必要としない場合は、リンクされたリストに裏打ちされたaConcurrentLinkedDequeまたは aを使用し、 を実装しないため、メソッドがありません (使用していないようです)。いずれにせよ、これらのコレクションによって返される反復子はフェイルファストではありませんが、一貫性が弱いです (つまり、反復子が作成されたときのコレクションを「見る」か、後の変更の一部を反映します)。LinkedBlockingDequeListget

もう 1 つの解決策は、並行コレクション ( ConcurrentLinkedDeque) と ReadWriteLock を使用することですが、より独創的な方法です。書き込みスレッドは読み取りロックを使用します (したがって、同時に書き込むことができます)。プリンター スレッドは書き込みロックを取得し (他のスレッドを一時的にブロックします)、コレクションのコピーを非並行コレクションに作成し、書き込みロックを解放して、最後にそのローカル コピーを出力します。

間違いなくよりスマートな (そしておそらくより高速な) 選択は、各スレッドに異なる非並行リストを割り当てることです。したがって、それぞれが単一のスレッドに割り当てられた非並行リストのリストがあります。すべてのスレッドが完了したら、すべてのリストを 1 つのリストに結合できます。さらに、各リストはロックで保護する必要があります。プリンター スレッドが印刷する場合、リストを繰り返し処理し、一度に 1 つずつロックし、コピーを作成し、ロックを解除してから印刷します。

私がコピーを作成することにこだわる理由は、I/O を印刷するよりもはるかに高速だからです。ワーカー スレッドがプリンター スレッドがリストを出力するのを待たなければならない場合、すべての計算が遅くなります。

PS: 唯一間違っているのが例外だとしても、ArrayLists はスレッド セーフではありません。複数のスレッドから同じリストに要素を追加すると、一部の要素が失われます (奇妙な例外が発生しない限り)。

于 2013-08-14T21:13:58.543 に答える
0

synchronizedまたは、 の減速に追加するだけですrun()synchronized{...}代わりに、スリープ/待機が「同期ゾーン」、つまり一度に 1 つのスレッドしか操作できない場所にある限り 、メソッドの内容をブロックで囲みます。

立ち往生している何かのためにこれを読んでいる人は、次のことを読んでください :
http://docs.oracle.com/javase/tutorial/essential/concurrency/sync.html

于 2013-08-14T20:36:40.627 に答える