web-dev-qa-db-ja.com

同期配列リストを作成する方法

私はこのような同期されたarrayListを作成しました

import Java.text.SimpleDateFormat;
import Java.util.*;


class HelloThread  
{

 int i=1;
 List arrayList;
  public  void go()
  {
 arrayList=Collections.synchronizedList(new ArrayList());
 Thread thread1=new Thread(new Runnable() {

  public void run() {
  while(i<=10)
  {
   arrayList.add(i);
   i++;
  }
  }
 });
 thread1.start();
 Thread thred2=new Thread(new Runnable() {
  public void run() {
     while(true)
     {
   Iterator it=arrayList.iterator();
      while(it.hasNext())
      {
       System.out.println(it.next());
      }
     }
  }
 });
 thred2.start();
  }
 }

public class test
{
  public static void main(String[] args)
  {
   HelloThread hello=new HelloThread();
   hello.go();
  }
}

しかし、このような例外を取得します

スレッド「Thread-1」の例外Java.util.ConcurrentModificationException

私のアプローチに何か問題がありますか?

12
Lalchand

IteratorsynchronizedListは同期されていません(同期できません)。反復中にリストを手動で同期する必要があります( javadoc を参照)。

_synchronized(arrayList) {
    Iterator it=arrayList.iterator(); 
    while(it.hasNext()) { 
        System.out.println(it.next()); 
   } 
}
_

別のアプローチは、Collections.synchronizedList()の代わりに CopyOnWriteArrayList を使用することです。コピーオンライトセマンティクスを実装しているため、同期は必要ありません。

26
axtavt

他の回答が問題を特定しました:

  • 同期されたコレクションのイテレータは同期されません。実際、これらはラッパークラス内のコレクションオブジェクトによって返されるイテレータにすぎません。

  • 多くのコレクションクラス(ArrayListを含む)は、フェイルファストメカニズムを使用して、反復中の同時変更を検出します。この動作は、それぞれのクラスのjavadocsに明確に文書化されています。これはあなたが見ているものです。

すべてのコレクションクラスがこれを行うわけではありません。たとえば、_Java.util.Concurrent..._コレクションクラスの多くは、反復中に同時変更を許可しますが、変更の結果が可能かどうかになるように、反復シーケンスのセマンティクスを緩和します。イテレータによって返されるオブジェクトで明らかになります。

Collections.synchronizedList() のjavadocは、イテレータを同期する方法を説明しています。基本的にあなたはこれをします:

_List list = Collections.synchronizedList(new ArrayList());
  ...
synchronized (list) {
    Iterator i = list.iterator(); // Must be in synchronized block
    while (i.hasNext())
        foo(i.next());
}
_

(余談ですが、通常、このようなことを行うとうまくいくと想定するのは安全ではありません。理論的には、同期リストはプライベートロックオブジェクトを使用でき、synchronizedステートメントは同時変更をロックアウトしません。ただし、javadocsこの場合、これが何をすべきかを言う...だから安全です。)

これを行う際の問題は、コレクションをロックすると同時実行のボトルネックが発生する可能性があることです。代わりに、コレクションの関連部分のコピーを内部的に作成するコピーオンライトデータ構造を使用することもできます。このアプローチは、イテレータがコレクションのスナップショットを確認することを意味します。反復と同時にコレクションに変更を加えることができますが、イテレーターはそれらを認識しません。コピーオンライトの問題は、変更が潜在的にはるかに高価になることです。

最終的には、同時変更と実際の要件の異なるコレクションタイプの特性とコストのバランスをとる必要があります。イテレータがすべての同時変更を確認できないことを回避できますか?

11
Stephen C

スレッドセーフな CopyOnWriteArrayList の使用を検討してください。アイテムを追加するたびに、基になる配列の新しいコピーが作成されます。ただし、イテレータは、イテレータが作成されてからリストへの追加を反映しませんが、ConcurrentModificationExceptionをスローしないことが保証されています。

arrayList=new CopyOnWriteArrayList();
10
dogbane

Java.util.ConcurrentModificationExceptionは、同じコレクションを反復処理しているときにコレクションを操作(追加、削除)すると発生します。

最初のスレッドで作成された後、2番目のスレッドで作成エントリを使用することをお勧めします。したがって、ArrayLists get( index )およびsize()を制御に使用できます。

3
stacker

Spikeが言ったように、コレクションを繰り返しながら変更することはできません。ただし、解決策は、反復中にリストをロックすることだと思います。

class HelloThread  
{

 int i=1;
 List arrayList;
  public  void go()
  {
 arrayList=Collections.synchronizedList(new ArrayList());
 Thread thread1=new Thread(new Runnable() {

  public void run() {
  while(i<=10)
  {
synchronized(someLock) {
   arrayList.add(i);
}
   i++;
  }
  }
 });
 thread1.start();
 Thread thred2=new Thread(new Runnable() {
  public void run() {
     while(true)
     {
synchronized(someLock) {
   Iterator it=arrayList.iterator();
      while(it.hasNext())
      {
       System.out.println(it.next());
      }
}
     }
  }
 });
 thred2.start();
  }
 }

public class test
{
  public static void main(String[] args)
  {
   HelloThread hello=new HelloThread();
   hello.go();
  }
}

あなたが何をしようとしているのかわからないので、これがあなたのコードの機能を壊さないことを願っています。

2

通常のリスト(ArrayListクラスによって実装される)を取得して、同期させましょう。これは、SynchronizedArrayListクラスに表示されます。 Collections.synchronizedListメソッドに新しい文字列のArrayListを渡します。このメソッドは、同期された文字列のリストを返します。 //ここにSynchronizedArrayListクラスパッケージcom.mnas.technology.automation.utility;があります。インポートJava.util.ArrayList; Java.util.Collectionsをインポートします。 Java.util.Iteratorをインポートします。インポートJava.util.List; import org.Apache.log4j.Logger;/** * * @author manoj.kumar * @email [email protected] * */public class SynchronizedArrayList {static Logger log = Logger.getLogger(SynchronizedArrayList.class.getName()); public static void main(String [] args){

List<String> synchronizedList = Collections.synchronizedList(new ArrayList<String>());
synchronizedList.add("Aditya");
synchronizedList.add("Siddharth");
synchronizedList.add("Manoj");

// when iterating over a synchronized list, we need to synchronize access to the synchronized list
synchronized (synchronizedList) {
Iterator<String> iterator = synchronizedList.iterator();
while (iterator.hasNext()) {
log.info("Synchronized Array List Items: " + iterator.next());
}
}

}
}

Notice that when iterating over the list, this access is still done using a synchronized block that locks on the synchronizedList object. 
In general, iterating over a synchronized collection should be done in a synchronized block
0
user4423251

反復しているコレクションを変更することはできません。これを回避するには、イテレータではなく、インデックスで配列エントリにアクセスします。このコードで解決しようとしている問題を教えていただければ、さらにアドバイスを提供できます。

0
Spike Gronim