java - 下面这段代码存在并发陷阱???
PHP中文网
PHP中文网 2017-04-18 10:10:31
0
2
545

曾宪杰的《大型网站系统与Java中间件实践》第一章第1.2.2.3小节给出以下代码示例:

使用HashMap数据被进行统计;

public class TestClass
{
    private HashMap<String, Integer> map = new HashMap<>();
    
    public synchronized void add(String key)
    {
        Integer value = map.get(key);
        if(value == null)
        {
            map.put(key, 1);
        }
        else
        {
            map.put(key, value + 1);
        }
    } 
}

使用ConcurrentHashMap保存数据并进行统计;

public class TestClass
{
    private ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
    
    public void add(String key)
    {
        Integer value = map.get(key);
        if(value == null)
        {
            map.put(key, 1);
        }
        else
        {
            map.put(key, value + 1);
        }
    } 
}

使用HashMap时,对add方法加锁,此时该方法是线程安全的,为何换为ConcurrentHashMap之后,原书中说存在并发陷阱???

PHP中文网
PHP中文网

认证高级PHP讲师

全員に返信(2)
巴扎黑

ConcurrentHashMap に変更した後、add メソッドをロックする必要があるのはなぜですか? ? ?

リーリー

ロックされていません。

編集:

確かに同時実行のトラップは存在します。次の状況を考えてみましょう:

  1. スレッド A は、map.get(key);if(value == null) を実行して結果 true を取得し、CPU 時間を引き渡します。

  2. このとき、スレッドAはまだmap.put(key, 1)を実行しておらず、スレッドBはmap.put(key, 1)を実行しているため、スレッドBも同じ場所に実行され、結果もtrueとなります。 . この時点で、マップにはすでにキー値が存在します。

  3. スレッドAは実行を継続するためにCPU時間を取得します。 前回の判定結果が真だったので、スレッドAは再度実行を開始します。最終的な結果として、両方のスレッドが map.put(key, 1) を 1 回実行します。この時点では key の値はまだ 1 ですが、実際には 2 になるはずです。

この問題が存在する理由は、ConcurrentHashMap の単一操作はアトミックですが、外部呼び出しは互いに独立した 2 つの操作であるため、スレッドのセキュリティを確保したい場合でも同様です。 get 操作と put 操作のアトミック性を確保するためにコードをロックする必要があります。

いいねを押す +0
左手右手慢动作

ConcurrentHashMap は、同時条件下で内部データの一貫性を維持できることのみを保証しますが、HashMap では保証できません。

ただし、add メソッドは典型的な Check-Then-Act または Read-Modify-Write であるため、スレッドセーフではありません。

クラス内のすべてのフィールドがスレッド セーフ クラスである場合、このクラスはスレッド セーフであるかどうかについて考えてみましょう。答えは明らかにノーです。

いいねを押す +0
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート