1

この質問は、整数値の同期に基づいてい ます。

から値を削除する方法に関する懸念に対処しない小さな問題があるだけで、解決策は優れているようですConcurrentHashMap

それで、私がプログラムの下で行ったことに対処するために

import java.util.concurrent.ConcurrentHashMap;

public class Example {

    private final ConcurrentHashMap<Integer, Integer> concurrentHashMap = new ConcurrentHashMap<Integer, Integer>();

    public void doSomething(int i) {
        synchronized (getLockForId(i)) {
            concurrentHashMap.remove(i);
        }
    }

    public Integer getLockForId(int id) {
        concurrentHashMap.putIfAbsent(id, id); // I want to replace these two
                                                // operation with single one
                                                // since it seems the cause of
                                                // NPE
        return concurrentHashMap.get(id);
    }

    public static void main(String[] args) {
        final Example example = new Example();
        new Thread(new Runnable() {
            @Override
            public void run() {
                int i = 0;
                while (true) {
                    example.doSomething(++i);
                }
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                int i = 0;
                while (true) {
                    example.doSomething(++i);
                }
            }
        }).start();
    }
}

問題は、結果が常に になることNullPointerExceptionです。私の最初の分析は、null に割り当てられた値を削除しているため、NullPointerException. だから私は以下にしました

    Object obj = new Object();
    synchronized (obj) {
        obj = null;
    }

しかし、上記は結果になりませんNullPointerException。私の質問は、なぜNullPointerException上記のケースで投げているのですか?

しても

public Integer getLockForId(int id) {
   return concurrentHashMap.putIfAbsent(id, id); 
}

NullPointerException他に1つの戻り値がある場合にのみ値を返すため、それでも結果が返されますnull

4

3 に答える 3

8

そうです、それNullPointerException. 次のパターンを検討してください。

 Thread 1                     Thread 2

 putIfAbsent (puts)
 get (returns non-null)
 acquire monitor
                              putIfAbsent (doesn't put)
 remove (removes value)
                              get (returns null)
                              acquire monitor (bang!)

「値がnullに割り当てられる」ということでMap.getはありません。指定されたキーのエントリがない場合、nullを返すということです。

あなたのコードは実際には何も役に立たないので、何を推奨すべきかを知るのは難しい. 実際のコードで何を達成しようとしているのかを言うことができれば、より良い提案ができる可能性があります。

EDIT:Nikitaが指摘したように、の値を返すだけでputIfAbsentは機能しません.以前の値を返すかnull、存在しない場合-エントリの新しい値が必要です.

getLockId基本的に、操作に関してメソッドをアトミ​​ックにするために、マップへのアクセスを同期する必要があると思いますremove

于 2012-10-15T06:17:24.733 に答える
2

concurrentHashMapすべてのアクセスを同期にしようとすることができます。したがって、マップから値を取得するときは同期し、concurrentHashMapそれを削除するときです。このようなもの:

public void doSomething(int i) {
    synchronized (getLockForId(i)) {
        // do stuff
        synchronized (concurrentHashMap) {
            concurrentHashMap.remove(i);
        }
    }
}


public Integer getLockForId(int id) {
    synchronized (concurrentHashMap) {
        concurrentHashMap.putIfAbsent(id, id);
        return concurrentHashMap.get(id);
    }
}
于 2012-10-15T06:25:03.107 に答える