2016-10-11 2 views
2

私は処理したいオブジェクトを持つLinkedListを持っています。オブジェクトは別のスレッドから追加されますが、1つのスレッドだけがそれを削除/読み込みします。私はキューにオブジェクトを追加し、別のスレッドでスレッドセーフティの問題

private LinkedList<MyObject> queue = new LinkedList<>(); 

new Thread() 
{ 
    @Override 
    public void run() 
    { 
     while (!Thread.interrupted()) 
     { 
      if (!queue.isEmpty()) 
      { 
       MyObject first = queue.removeFirst(); 
       // do sth.. 
      } 
     } 
    } 
}.start(); 

queue.add(new MyObject()); 

時には、このコードは、私は本当に自分自身に説明カントた、しかし例外につながります。 java.util.LinkedList.removeFirst(LinkedList.java:270)私はこの例外を取得する理由はそれだけで削除しようとする必要があるため、取得いけない

でjava.util.NoSuchElementException 「」スレッドで 例外存在する場合はオブジェクト。

+1

キューが空の場合、消費者スレッドは「ビジー待機」を行います。おそらくそれも修正するべきです。 –

+0

少し愚かですが、これらのことは予測が非常に難しいことがあるので、実際にremoveFirstメソッドを呼び出すスレッドは1つだけであることを余儀なくされることをお勧めします。 if文の中にスレッドIDのプリントを追加します。 – TheFooBarWay

+0

@ TheFooBarWay消費者が1人か2人かは関係ありません。同期の問題を引き起こすのに十分な複数のプロデューサが存在します。オブジェクトを変更するスレッドが複数ある場合は、同期させる必要があります。正確に1つのスレッドの変更と複数のスレッドの読み取りがあった場合は、非同期になる可能性があります(反復は難しいかもしれません)。 –

答えて

2

Nicolasはすでに述べたように、スレッドセーフな実装が必要です。 LinkedBlockingQueueを使用することをお勧めします。

offerメソッドを使用して追加し、takeを使用して削除すると、「ビジー待機」の問題も解決されます。あなたは何ができるか

+1

真実ですが、実際の質問には答えていますが、これは私たちが例外で終わる方法です。もし実際にアイテムを取り除いているスレッドが1つだけあれば、ifステートメントでコードを線形化でき、実行は合法でなければなりません。 – TheFooBarWay

+2

@ TheFooBarWay実装の詳細に行き、どのような割り当てがどこで起きるか調べるのは価値がないとは思わないが、それは非同期であるため、このような問題が発生する可能性がある。 –

+1

プログラマーの視点から見れば、多分努力する価値はありません。しかし、これらはしばしば興味深い学問的質問です。 私は思ったとおりニックピッキングしていますが、スレッドセーフなソリューションを提案するときにはここで本当の疑問を抱かないように感じます。それは子供に電卓を使って長時間の分割を行う方法を示すようなものです。 あなたが正しいこと以外は。 – TheFooBarWay

0

LinkedListは、現在そうでないあなたの代わりに使用し、一貫性のない状態につながる同時変更には、このような予測不可能なバグに直面することになるんように、あなたが複数のスレッドでそれを共有することはできませんスレッドセーフではありませんスレッドセーフデキューConcurrentLinkedDequeなどです。

+0

ところで、私はあなたにdownvoteしなかったし、なぜ誰かが知りませんでした。 私はそれをキャンセルするupvoted。実際には、これとJaroslaw Pawlakの回答の間で、選択された回答を選択するのは苦労しました。 – BluE

-2

等ミューテックス、セマフォ、モニタ、メールボックスのように、スレッドを調整するための技術のいくつかの種類を使用することです

0

私が解決する方法のように提供いくつかの良い解決策があったと思いますが、 @BluEにNoSuchElementExceptionが表示される理由は何も解明されていません。だから私は起こっていると思います。

LinkedListのアクセスが同期されていないので、可能性がある:(!queue.isEmpty())プロデューサーのスレッドが

  • 二つの消費者のスレッドが同時にある場合をチェックして、参照キューに要素を追加

    1. それはそうではありません。
    2. 両方のコンシューマスレッドは、MyObjectを呼び出すキューから要素を取得しようとします。first = queue.removeFirst();
    3. キューに要素がもうないため、スレッドの1つが成功し、もう1つがNoSuchElementExceptionで失敗します。

      UPDATE:

    あなたが唯一のプロデューサーと1人の消費者を持って提供、私はJavaのメモリモデルの仕様は、あなたが見るの挙動を説明することができると思います。

    LinkedListへのアクセスが同期されていないため、JVMによって提供されるデータの可視性の保証はありません。AbstractCollection

    public boolean isEmpty() { 
        return size() == 0; 
    } 
    

    から

    transient int size = 0; 
    transient Node<E> first; 
    
    // ... 
    
    public int More ...size() { 
        return size; 
    } 
    
    // ... 
    
    public E removeFirst() { 
        final Node<E> f = first; 
        if (f == null) 
         throw new NoSuchElementException(); 
        return unlinkFirst(f); 
    } 
    

    LinkedListの

    から

    あなたが見ることができるように、サイズや要素が異なる変数に格納されていますのは、のisEmptyとremoveFirstとメソッドの実装を見てみましょう。したがって、コンシューマスレッドは "size"変数の更新を見て、 "first"の更新を表示しない可能性があります。

  • +1

    あなたの推測に感謝しますが、私は消費者が1人しかないので、それはできません。 – BluE

    関連する問題