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

}

在上面的代码中,当put()和remove()时,使用readLock而不是writeLock,它们是最常用的;当open()和close()时,它们都使用writeLock,它们的使用率较低。 目标是提高并发性。 我不确定:

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

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

线程安全性:

从某种意义上说,它是线程安全的。 调用close之后,进一步的putremove调用将不会影响concurrentHashMap引用的地图的状态。

但是,在下一次open之前调用putremove将导致更新丢失。 考虑到openclose是避免丢失更新,因此这让我感到很不满意。 这可能是另一个级别的线程安全问题。

效率:

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

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


我想我会使用AtomicReferencejavadoc )...来实现它,并且没有锁。 诀窍是使用ref.getAndSet(new ConcurrentHashMap())将现有地图“切换”为新的空地图。

AtomicReference仍将是并发瓶颈,但程度要小得多,并且可以通过将这两个操作作为单个原子操作进行操作来避免“关闭...打开”漏洞。

有关使用AtomicReference的示例解决方案,请参见@Holger的答案...,请注意,他的版本无法解决“封闭...裸眼”问题。

正如其他人所说,可以通过针对此用例使用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;
}

暂无
暂无

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

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