简体   繁体   English

这个线程在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;
}

} }

In the code above, when put() and remove(), use readLock instead of writeLock, they are most frequently used.When open() and close(), both use writeLock, they are less frequently used. 在上面的代码中,当put()和remove()时,使用readLock而不是writeLock,它们是最常用的;当open()和close()时,它们都使用writeLock,它们的使用率较低。 The target is to improve concurrency. 目标是提高并发性。 I am not sure about: 我不确定:

  1. Is it thread safe? 它是线程安全的吗?
  2. Is it efficient? 有效率吗?

I think both is yes. 我认为两者都是。 I know ConcurrentHashMap is thread safe. 我知道ConcurrentHashMap是线程安全的。 I want to know if this implementation is good or bad and why. 我想知道此实现的好坏,为什么。

Thread-safety: 线程安全性:

It is thread-safe in one sense. 从某种意义上说,它是线程安全的。 Once close has been called, further calls to put and remove won't affect the state of the map that concurrentHashMap refers to. 调用close之后,进一步的putremove调用将不会影响concurrentHashMap引用的地图的状态。

However, calls to put and remove before the next open will lead to lost updates. 但是,在下一次open之前调用putremove将导致更新丢失。 That strikes me as poor design ... given that the ostensible point of open and close is to avoid losing updates. 考虑到openclose是避免丢失更新,因此这让我感到很不满意。 This could be a thread-safety issue at another level. 这可能是另一个级别的线程安全问题。

Efficiency: 效率:

On the one hand: I observe that all updates to the map are performed while you hold the lock. 一方面:我观察到在您持有锁的同时对地图的所有更新都已执行。 Given that, I don't think there is any point in using a ConcurrentHashMap . 鉴于此,我认为使用ConcurrentHashMap没有任何意义。 Using a regular HashMap would be thread-safe and more efficient. 使用常规的HashMap将是线程安全的,并且效率更高。

On the other hand, since all updates are performed while holding the lock, the lock is a concurrency bottleneck, and the potential concurrency advantages of using a ConcurrentHashMap are moot. 另一方面,由于所有更新都是在持有该锁的同时执行的,因此该锁是并发瓶颈,使用ConcurrentHashMap的潜在并发优势尚无定论。


I think I would implement this using an AtomicReference ( javadoc ) ... and no lock. 我想我会使用AtomicReferencejavadoc )...来实现它,并且没有锁。 The trick is to use ref.getAndSet(new ConcurrentHashMap()) to "switch" the existing map for a new empty one. 诀窍是使用ref.getAndSet(new ConcurrentHashMap())将现有地图“切换”为新的空地图。

The AtomicReference will still be a concurrency bottleneck, but to a much lesser degree, and you can avoid the "close ... open" hole by doing the two actions as a single atomic operation. AtomicReference仍将是并发瓶颈,但程度要小得多,并且可以通过将这两个操作作为单个原子操作进行操作来避免“关闭...打开”漏洞。

See @Holger's answer for an example solution using AtomicReference ... noting that his version doesn't address the "close ... open hole" problem. 有关使用AtomicReference的示例解决方案,请参见@Holger的答案...,请注意,他的版本无法解决“封闭...裸眼”问题。

As said by others, the efficiency can be improved by using an AtomicReference for this use case. 正如其他人所说,可以通过针对此用例使用AtomicReference来提高效率。 But, what's perhaps even more important, the code becomes much simpler: 但是,更重要的是,代码变得更加简单:

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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM