2016-07-14 5 views
2

を入れて、私はスレッドがHashMap.put上で立ち往生している参照してください。HashMapのは、上の立ち往生、私は、マルチスレッド環境でコーディングしています

34 Threads 
java.util.HashMap.put(HashMap.java:374) 
com.aaa.bbb.MyClass.getDefinitionMap(). 

HashMapが、私は方法が同期されていることを見ている方法を調査:

@Override 
public synchronized Map<String,String> getDefinitionMap() { 
     //truncated some code here... 
     colDefMap = new HashMap<String,String>();   
     for (CD cd : (List<CD>)cm.getDef()) { 
      colDefMap.put(cd.getIdentifier(),cd); 
     } 
    return colDefMap; 
} 

ConcurrentHashMapに切り替えた後、メソッドのシグネチャからsynchronizedキーワードを削除し、アプリケーションサーバーを再起動すると問題が解決します。

私の質問は、このシナリオで同時アクセスからマップを保護するには同期メソッドでは十分ではないのですか?

+3

[mcve]なしでは非常にわかりにくいです。あなたのequals/hashCodeメソッドは同期されているものを呼び出します。デッドロックが発生する可能性がありますか? –

+4

あなたは正確にアクセスを同期していますか?このメソッドで 'HashMap'を作成しているので、このメソッドが別のスレッドのマップに干渉するリスクはありません。共有状態へのアクセスを同期する必要があるだけなので、 'cm'へのアクセスを同期させる必要がありますか? – Samuel

+1

おそらくどこかでデッドロックが発生する可能性があります。あなたは、また、同期呼び出しを持つかもしれないあなたのすべてのメソッドをチェックする必要があります。確かに知るには十分なコードがありません。しかし、そのクラスに他のメソッドが同期されているのであれば、私は興味があります。 'cm'はどうですか?そこにはロックがありますか? 。 –

答えて

2

実際にデッドロックやボトルネックがあるかどうかにかかわらず、どのように「スタック」しているかはわかりません。

ポストされたコードはボトルネックになります。ほとんどのスレッドが同じオブジェクトにアクセスしようとしていて、synchronizedメソッドで使用されているロックの取得を待っています。 cm.getDefの処理には時間がかかり、一度に1つのスレッドしか処理を行えない可能性があります。同期化は、スループットを犠牲にして、同時アクセスからデータを保護します。

これはthe Java concurrency tutorialに与えられた「飢餓」の定義に合う:

飢餓は、スレッドが共有リソースへの定期的なアクセスを得ることができず、進歩を遂げることができない状況を説明しています。これは、共有リソースが「貪欲」スレッドによって長期間使用できなくなった場合に発生します。たとえば、オブジェクトが同期メソッドを提供し、返されるまでに時間がかかることが多いとします。あるスレッドがこのメソッドを頻繁に呼び出した場合、同じオブジェクトに頻繁に同期アクセスする必要がある他のスレッドもしばしばブロックされます。

ConcurrentHashMapに切り替えると、わかりやすく改善されます。回収のと更新のための高予想される同時実行の完全な同時実行をサポートする

ハッシュテーブル:ConcurrentHashMapのはthe API doc(私の強調)を参照してください、マップ全体のうち、スレッドをロック回避し、同時更新をサポートしています。このクラスは、Hashtableと同じ機能仕様に従い、Hashtableの各メソッドに対応するメソッドのバージョンを含みます。しかし、すべての操作がスレッドセーフであっても、検索操作はロックを伴わず、すべてのアクセスを妨げるようにテーブル全体をロックするためのサポートはありません。このクラスは、スレッドの安全性に依存するが、同期の詳細に依存しないプログラムでは、Hashtableと完全に相互運用可能です。

いつでもcm.getDefを呼び出す必要がないようにキャッシュすることを検討することもできますが、その実用性はもちろん要件によって異なります。

+0

ボトルネックがあった場合、ConcurrentHashMapは問題を解決しませんが、正しいですか? – user648026

+1

@ user648026:ロックオンが劇的に少なくなるため、問題を解決する必要があります。 –

2

あなたは明らかにcmへのアクセス権を持っている唯一の方法(またはクラス)ないサブクラスでgetDefinitionMap方法、上で同期されました。あなたはそれを反復している間cm変数はおそらく修正されている、上記のコードで

for (CD cd : (List<CD>) cm.getDef()) 
{ 
    colDefMap.put(cd.getIdentifier(), cd); 
} 

cm変数クラスの反復子は、おそらく犯人です。

次を使用することもできました:

synchronized (cm) 
{ 
    for (CD cd : (List<CD>) cm.getDef()) 
    { 
     colDefMap.put(cd.getIdentifier(), cd); 
    } 
} 

cmへの変更は、同様の同期せずに行った場合ただし、これはまだ、他のスレッドに開放cmの変更を残しているだろう。

発見したように、マルチスレッド環境でスレッドセーフではないコレクションの回避策を実装するよりも、スレッドセーフバージョンのコレクションクラスを使用する方がずっと簡単です。

+1

したがって、メソッドのシグネチャに同期を設定しても、競合状態は防止されません。 – user648026

+1

@ user648026その質問に答えるには、あなたが競争する可能性のあるものを検討する必要があります。メソッドを 'synchronized'に設定すると、そのメソッドの同時呼び出しはできません。しかし、他の方法では、その変数を独立して変更することがあり、両方のメソッドで 'synchronized'を設定しても問題は解決しません。2つのドア(前後)を持つ家を想像して、各ドアにドアマンを置いて、一度に1人の人しか家に入れないように指示します。ドアマンはお互いに話していないし、家に二人で終わる。 – vallismortis

1

他の場所で変更していますか?あなたはそれがputではないことを100%確信していますか?私はあなたがいると思うし、第二のputが無限ループを引き起こしている可能性が高い。 http://mailinator.blogspot.com/2009/06/beautiful-race-condition.html

これ以外の場合は、HashMapを変更する唯一の場所であれば問題ありません。

2

あなたの問題を解決すると思って間違っていると思います。 を同期化してを削除すると、この方法へのアクセスをロック解除することができ、問題を解決して他のものを呼び出すことができます。私はあなたのHashMapが関数のスコープ内に作成されていることを意味するので、ここには並行性の問題があるはずはありません(内部に置かれているものが静的でないかスレッドセーフではない)。 concurrentHashMapを使用することは決して効果がありません。

あなたの関数がsynchronized文がないジョブ(concurrentMapなし)であれば、マルチスレッド・テストで試してみることをお勧めします。

私の意見では、残りのコードを知らないと、この関数はスレッドによってロックされる静的または共有データにアクセスしている可能性があります。そのため、問題は関数から来ないので、 。

関連する問題