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,使用频率较低用过的。目标是提高并发性。我不确定:
- 它是线程安全的吗?
- 效率高吗?
我认为两者都是。我知道 ConcurrentHashMap 是线程安全的。我想知道这个实现是好是坏,为什么。
线程安全:
它在某种意义上是线程安全的。一旦 close
被调用,对 put
和 remove
的进一步调用将不会影响 concurrentHashMap
所指的地图的状态。
但是,在下一个 open
之前调用 put
和 remove
将导致丢失更新。考虑到 open
和 close
表面上的要点是避免丢失更新,这让我觉得这是糟糕的设计。这可能是另一个层面的线程安全问题。
效率:
一方面:我观察到地图的所有更新都是在您持有锁的情况下执行的。鉴于此,我认为使用 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;
}
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,使用频率较低用过的。目标是提高并发性。我不确定:
- 它是线程安全的吗?
- 效率高吗?
我认为两者都是。我知道 ConcurrentHashMap 是线程安全的。我想知道这个实现是好是坏,为什么。
线程安全:
它在某种意义上是线程安全的。一旦 close
被调用,对 put
和 remove
的进一步调用将不会影响 concurrentHashMap
所指的地图的状态。
但是,在下一个 open
之前调用 put
和 remove
将导致丢失更新。考虑到 open
和 close
表面上的要点是避免丢失更新,这让我觉得这是糟糕的设计。这可能是另一个层面的线程安全问题。
效率:
一方面:我观察到地图的所有更新都是在您持有锁的情况下执行的。鉴于此,我认为使用 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;
}