java 这个线程安全高效吗

Is this thread safe and efficient in java

public class Test {

static ConcurrentHashMap<Integer, Integer> map = null;
final static ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

public static void open() {

    lock.writeLock().lock();
    try {
        if (map != null) {
            return;
        }
        map = new ConcurrentHashMap<>();
    } finally {
        lock.writeLock().unlock();
    }
}

public static void close() {

    final ConcurrentHashMap<Integer, Integer> concurrentHashMap;

    lock.writeLock().lock();
    try {
        if (map == null) {
            return;
        }
        concurrentHashMap = map;
        map = null;
    } finally {
        lock.writeLock().unlock();
    }

    // deal with concurrentHashMap data
}

public static boolean put(final int key, final int value) {
    lock.readLock().lock();
    try {
        if (map == null) {
            return false;
        }
        if (map.putIfAbsent(key, value) != null) {
            return false;
        }
    } finally {
        lock.readLock().unlock();
    }
    return true;
}

public static boolean remove(final int key) {
    lock.readLock().lock();
    try {
        if (map == null) {
            return false;
        }
        if (map.remove(key) == null) {
            return false;
        }
    } finally {
        lock.readLock().unlock();
    }
    return true;
}

}

上面代码中,put()和remove()时,使用readLock而不是writeLock,使用频率最高 used.Whenopen()和close(),都使用writeLock,使用频率较低用过的。目标是提高并发性。我不确定:

  1. 它是线程安全的吗?
  2. 效率高吗?

我认为两者都是。我知道 ConcurrentHashMap 是线程安全的。我想知道这个实现是好是坏,为什么。

线程安全:

它在某种意义上是线程安全的。一旦 close 被调用,对 putremove 的进一步调用将不会影响 concurrentHashMap 所指的地图的状态。

但是,在下一个 open 之前调用 putremove 将导致丢失更新。考虑到 openclose 表面上的要点是避免丢失更新,这让我觉得这是糟糕的设计。这可能是另一个层面的线程安全问题。

效率:

一方面:我观察到地图的所有更新都是在您持有锁的情况下执行的。鉴于此,我认为使用 ConcurrentHashMap 没有任何意义。使用常规 HashMap 将是线程安全的并且效率更高。

另一方面,由于所有更新都是在持有锁的情况下执行的,因此锁是并发瓶颈,使用 ConcurrentHashMap 的潜在并发优势尚无定论。


我想我会使用 AtomicReference (javadoc) 来实现它......并且没有锁定。诀窍是使用 ref.getAndSet(new ConcurrentHashMap()) 将现有地图 "switch" 用于新的空地图。

AtomicReference 仍将是并发瓶颈,但程度要小得多,您可以通过将两个操作作为单个原子操作来避免 "close ... open" 漏洞。

请参阅@Holger 的回答以获取使用 AtomicReference 的示例解决方案...注意他的版本没有解决 "close ... open hole" 问题。

正如其他人所说,对于此用例,可以通过使用 AtomicReference 来提高效率。但是,也许更重要的是,代码变得更加简单:

static final AtomicReference<ConcurrentHashMap<Integer, Integer>>
    MAP = new AtomicReference<>();

public static void open() {
    MAP.compareAndSet(null, new ConcurrentHashMap<>());
}

public static void close() {
    ConcurrentHashMap<Integer, Integer> map = MAP.getAndSet(null);
    if(map != null) {
        // deal with map data
    }
}

public static boolean put(final int key, final int value) {
    ConcurrentHashMap<Integer, Integer> map = MAP.get();
    return map != null && map.putIfAbsent(key, value) == null;
}

public static boolean remove(final int key) {
    ConcurrentHashMap<Integer, Integer> map = MAP.get();
    return map != null && map.remove(key) != null;
}