ArrayListをスレッドセーフにするにはどうすればよいですか?Javaの問題に対する別のアプローチ?


90

実行が終了したらすぐにThreadクラスを拡張するRaceCarオブジェクトを保持するために使用するArrayListがあります。Raceと呼ばれるクラスは、RaceCarオブジェクトが実行の終了時に呼び出すコールバックメソッドを使用して、このArrayListを処理します。コールバックメソッドaddFinisher(RaceCar finisher)は、RaceCarオブジェクトをArrayListに追加します。これは、スレッドが実行を終了する順序を与えることになっています。

ArrayListは同期されていないため、スレッドセーフではありません。新しいArrayListを渡し、返されたコレクションをArrayListに割り当てることで、Collections.synchronizedCollection(c Collection)メソッドを使用してみました。ただし、これによりコンパイラエラーが発生します。

Race.java:41: incompatible types
found   : java.util.Collection
required: java.util.ArrayList
finishingOrder = Collections.synchronizedCollection(new ArrayList(numberOfRaceCars));

関連するコードは次のとおりです。

public class Race implements RaceListener {
    private Thread[] racers;
    private ArrayList finishingOrder;

    //Make an ArrayList to hold RaceCar objects to determine winners
    finishingOrder = Collections.synchronizedCollection(new ArrayList(numberOfRaceCars));

    //Fill array with RaceCar objects
    for(int i=0; i<numberOfRaceCars; i++) {
    racers[i] = new RaceCar(laps, inputs[i]);

        //Add this as a RaceListener to each RaceCar
        ((RaceCar) racers[i]).addRaceListener(this);
    }

    //Implement the one method in the RaceListener interface
    public void addFinisher(RaceCar finisher) {
        finishingOrder.add(finisher);
    }

私が知る必要があるのは、私は正しいアプローチを使用していますか?そうでない場合は、コードをスレッドセーフにするために何を使用する必要がありますか?助けてくれてありがとう!


2
(注意:Listインターフェースはマルチスレッドで非常に役立つほど完全ではありません。)
Tom Hawtin-タックライン

3
私が指摘してCollections.synchronizedList()おきたいのは、なしでは、ここで実際の競合状態が発生するということです:P
ディランワトソン14年

回答:


145

を使用しCollections.synchronizedList()ます。

例:

Collections.synchronizedList(new ArrayList<YourClassNameHere>())

2
ありがとう!ベクターが同期された場所で読んだことを覚えているので、ベクターを使用することを考えなかった理由がわかりません。
ericso

32
非推奨として定義されたクラスを操作するのは良い考えではないかもしれません
frandevel

1
Vectorは非常に古く、コレクションのサポートはありませんが、非推奨ではありません。他の人がここで言ったように、おそらくCollections.synchronizedList()を使用する方が良いでしょう。
アストゥリオ2013

14
コメントの場合は-1。ベクターは非推奨ではなく、コレクションのサポートがないのはなぜですか?リストを実装します。Vectorのjavadocは特に次のように述べています。「Java 2プラットフォームv1.2以降、このクラスはListインターフェースを実装するために改良され、Java Collections Frameworkのメンバーになりました。新しいコレクションの実装とは異なり、Vectorは同期されます。」Vectorを使用しない(同期を回避し、実装を変更する)には十分な理由があるかもしれませんが、「時代遅れ」または「最新ではない」ことはそれらの1つではありません。
fool4jesus 2013年

1
以下のメソッドを使用します。Collections.synchronizedList(list); Collections.synchronizedSet(set); Collections.synchronizedMap(map); 上記のメソッドは、コレクションをパラメーターとして受け取り、同期され、スレッドセーフな同じタイプのコレクションを返します。
Sameer Kazi、2015

35

変化する

private ArrayList finishingOrder;

//Make an ArrayList to hold RaceCar objects to determine winners
finishingOrder = Collections.synchronizedCollection(new ArrayList(numberOfRaceCars)

private List finishingOrder;

//Make an ArrayList to hold RaceCar objects to determine winners
finishingOrder = Collections.synchronizedList(new ArrayList(numberOfRaceCars)

ListはArrayListのスーパータイプなので、指定する必要があります。

そうでなければ、あなたがやっていることは問題ないようです。他のオプションは、同期されているベクターを使用できることですが、これはおそらく私がすることです。


1
またはListおそらくもっと便利でしょう。またはList<RaceCar>
トムホーティン-タックライン

良い点、それを非公開にします。finishOrder = Collections.synchronizedList(...)
ゴンゾ牧師、

私はこれを試してみましたが、コンパイラがコレクションでArrayListメソッドを呼び出すことについて不満を//Print out winner System.out.println("The Winner is " + ((RaceCar) finishingOrder.get(0)).toString() + "!"); 言っています: get(0)メソッドが見つからないと言っています。考え?
ericso

コメントの削除と再追加について申し訳ありません。バックティックを使用して強調表示を機能させようとしました。私はその種のものについてOCDを取得します。
ericso

いいえ、それはうまくいきません。それはコレクションをリストにキャストしません:Race.java:41:互換性のないタイプが見つかりました:java.util.Collectionが必要です:java.util.List finishingOrder = Collections.synchronizedCollection(new ArrayList(numberOfRaceCars));
ericso

11

CopyOnWriteArrayList

CopyOnWriteArrayListクラスを使用します。これはのスレッドセーフバージョンですArrayList


3
このクラスを検討するときは、よく考えてください。クラスドキュメントを引用するには:「これは通常、コストがかかりすぎますが、トラバーサル操作がミューテーションを大幅に上回っている場合は、他の方法よりも効率的かもしれません。 。」また、CopyOnWriteArrayListとsynchronizedListの違いを
Basil Bourque

このクラスは、リストをほとんど変更しないときに機能しますが、多くの場合、要素を繰り返し処理します。たとえば、リスナーのセットがある場合です。それらを登録してから、何度も繰り返します...リストインターフェイスを明示的に必要としないが、変更と読み取りの操作を並行して行う場合は、検討してくださいConcurrentLinkedQueue
benez

7

あなたは可能性が間違ったアプローチを使用すること。車をシミュレートする1​​つのスレッドが別の車シミュレーションスレッドの前に終了するからといって、最初のスレッドがシミュレートされたレースに勝つ必要があるわけではありません。

これはアプリケーションに大きく依存しますが、レースが完了するまで、短い時間間隔ですべての車の状態を計算する1つのスレッドを使用することをお勧めします。または、複数のスレッドを使用したい場合は、各車にレースの完了にかかった「シミュレーションされた」時間を記録させ、最短の勝者として勝者を選択することができます。


それは良い点です。これは、私がJavaを学ぶために使用しているテキストからの演習にすぎません。ポイントは、スレッドの使用方法を学ぶことでした。私は実際には、勝者を記録するメカニズムを構築する際に、問題の元の仕様を超えています。私はタイマーを使って勝者を測定することについて考えました。でも正直、演習で必要なものは手に入れたと思います。
ericso 2010年

5

このようなメソッドにsynchronizedキーワードを使用することもできますaddFinisher

    //Implement the one method in the RaceListener interface
    public synchronized void addFinisher(RaceCar finisher) {
        finishingOrder.add(finisher);
    }

したがって、この方法でスレッドセーフなArrayList addメソッドを使用できます。


4
では、addFinisherとdelFinisherの2つのメソッドがある場合はどうでしょうか。どちらの方法もスレッドセーフですが、どちらも同じArrayListにアクセスするため、問題が発生します。
masi

1
@masi次に、何らかの方法でにfinal Objectアクセスするたびに、代わりにで同期しますCollection
mkuech 2015年

2

antコレクションオブジェクトのantスレッドセーフバージョンを使用する場合は、java.util.concurrent。*パッケージを利用してください。同期されていないコレクションオブジェクトのほぼすべての同時バージョンがあります。例:ArrayListの場合、java.util.concurrent.CopyOnWriteArrayListがあります。

Collections.synchronizedCollection(任意のコレクションオブジェクト)を実行できますが、この従来の同期を覚えておいてください。テクニックは高価で、パフォーマンスのオーバーヘッドが伴います。java.util.concurrent。*パッケージは低コストであり、次のようなメカニズムを使用することでパフォーマンスをより適切に管理します

コピーオンライト、比較してスワップ、ロック、スナップショットイテレータなど

したがって、java.util.concurrent。*パッケージから何かを優先します


1

Vectorはスレッドセーフであり、arraylistはそうではないため、代わりにVectorとして使用することもできます。ベクトルは古いですが、目的を簡単に解決できます。

ただし、次のコードのように、Arraylistを同期させることができます。

Collections.synchronizedList(new ArrayList(numberOfRaceCars())); 

-1

すべてのメソッドが同期されるArrayListからVectorタイプに変更できます。

private Vector finishingOrder;
//Make a Vector to hold RaceCar objects to determine winners
finishingOrder = new Vector(numberOfRaceCars);

5
別のコレクションの使用を提案する場合は、おそらくベクターは適切な選択肢ではありません。これは、新しいJava Collections Frameworkの設計に改良されたレガシーコレクションです。java.until.concurrentパッケージにはより良い選択肢があると確信しています。
Edwin Dalorzo、2014年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.