问题:
- 如何保证集合是线程安全的?
- ConcurrentHashMap如何实现高效的线程安全?
知识点:
- 为什么需要ConcurrentHashMap?
Hashtable比较低效,所有线程公用同一把锁,效率低下。Collections工具类提供的Map方法,大同小异,源码如下:
private static class SynchronizedMap<K, V> implements Map<K, V>, Serializable {
private static final long serialVersionUID = 1978198479659022715L;
private final Map<K, V> m;
//划重点了,利用this作为互斥的mutex
final Object mutex;
private transient Set<K> keySet;
private transient Set<Entry<K, V>> entrySet;
private transient Collection<V> values;
SynchronizedMap(Map<K, V> var1) {
this.m = (Map)Objects.requireNonNull(var1);
this.mutex = this;
}
SynchronizedMap(Map<K, V> var1, Object var2) {
this.m = var1;
this.mutex = var2;
}
public int size() {
Object var1 = this.mutex;
synchronized(this.mutex) {
return this.m.size();
}
}
public boolean isEmpty() {
Object var1 = this.mutex;
//方法不再申明为synchronized方法,但是利用this作为互斥的mutex,大同小异 synchronized(this.mutex) {
return this.m.isEmpty();
}
}
高并发时候,容易始HashMap死循环导致CPU占用100%。可参考不正当使用HashMap导致cpu 100%的问题追究,
HashMap 死循环的探究
- ConcurrentHashMap,篇幅太长,后续有专门文章分析。
回答问题
Java提供了不同层面的线程安全支持,在传统的框架内部,除了Hashtable等同步容器,还提供了所谓的同步包装器,我们可以调用Collecions工具的同步方法,来获取一个同步的包装容器(如:Collections.synchronizedMap),但是他们都是利用非常粗粒度的处理方式,在高并发的时候,性能比较低下。
另外,更加普遍的选择是利用并发包提供的线程安全容器类,它提供了:
- 各种并发容器:比如ConcurrentHashMap、CopyOnWriteArrayList。
- 各种线程安全队列(Queue/Deque),如ArrayBlockingQueue、synchronousQueue。
- 各种有序容器的线程安全版本。
可参考:Java并发:线程安全的容器:同步和并发 java线程安全的容器有哪些?
具体保证线程安全的方式,包括从有简单的synchronize方式,到基于更加精细化的,比如基于分离锁实现的ConcurrentHashMap等并发实现等。具体选择要看开发的场景需求。总得来说,并发包提供的容器通用场景,远优于早期的简单的同步实现。
自己项目中使用代码:
private Map<Promise, ReadableMap> mPictureTakenOptions = new ConcurrentHashMap<>();
private Map<Promise, File> mPictureTakenDirectories = new ConcurrentHashMap<>();
public void takePicture(ReadableMap options, final Promise promise, File cacheDirectory) {
mPictureTakenPromises.add(promise);
mPictureTakenOptions.put(promise, options);
mPictureTakenDirectories.put(promise, cacheDirectory);
if (mPlaySoundOnCapture) {
MediaActionSound sound = new MediaActionSound();
sound.play(MediaActionSound.SHUTTER_CLICK);
}
try {
super.takePicture();
} catch (Exception e) {
mPictureTakenPromises.remove(promise);
mPictureTakenOptions.remove(promise);
mPictureTakenDirectories.remove(promise);
throw e;
}
}
参考:
- 不正当使用HashMap导致cpu 100%的问题追究,
- HashMap 死循环的探究
- Collections工具类中部分源码
- 极客时间APP核心技术第十讲| 如何保证集合是线程安全的?ConcurrentHashMap如何实现高效的线程安全?
- 自己项目中部分代码
声明:此为原创,转载请联系作者
作者:微信公众号添加公众号-遛狗的程序员 ,或者可以扫描以下二维码关注相关技术文章。
当然喜爱技术,乐于分享的你也可以可以添加作者微信号: