- 921. Minimum Add to Make Parentheses Valid 使括号有效的最少添加
- 915. Partition Array into Disjoint Intervals 分割数组
- 932. Beautiful Array 漂亮数组
- 940. Distinct Subsequences II 不同的子序列 II
public interface ConcurrentMap extends Map
1、 提供其他原子putIfAbsent、remove、replace方法的Map;
2、 内存一致性效果:当存在其他并发collection时,将对象放入ConcurrentMap之前的线程中的操作happen-before随后通过另一线程从ConcurrentMap中访问或移除该元素的操作;
接口方法
/**
* 如果指定键已经不再与某个值相关联,则将它与给定值关联。
*/
V putIfAbsent(K key, V value);
/**
* 只有目前将键的条目映射到给定值时,才移除该键的条目。
*/
boolean remove(Object key, Object value);
/**
* 只有目前将键的条目映射到某一值时,才替换该键的条目。
*/
boolean replace(K key, V oldValue, V newValue);
/**
* 只有目前将键的条目映射到给定值时,才替换该键的条目。
*/
V replace(K key, V value);
public class ConcurrentHashMap extends AbstractMap implements ConcurrentMap, Serializable
1、 支持获取的完全并发和更新的所期望可调整并发的哈希表;
2、 不允许将null用作键或值;
3、 适用于读多写少场景;
成员变量
/* ---------------- Constants -------------- */
/**
* 默认初始容量
*/
static final int DEFAULT_INITIAL_CAPACITY = 16;
/**
* 默认装载因子
*/
static final float DEFAULT_LOAD_FACTOR = 0.75f;
/**
* 默认更新并发线程数
*/
static final int DEFAULT_CONCURRENCY_LEVEL = 16;
/**
* 最大容量
*/
static final int MAXIMUM_CAPACITY = 1 << 30;
/**
* SEGMENT最大个数
*/
static final int MAX_SEGMENTS = 1 << 16; // slightly conservative
/**
* 锁之前重试次数
*/
static final int RETRIES_BEFORE_LOCK = 2;
/* ---------------- Fields -------------- */
/**
* 段标记
*/
final int segmentMask;
/**
* 段的偏移量
*/
final int segmentShift;
/**
* 特殊table-段
*/
final Segment<K,V>[] segments;
transient Set<K> keySet;
transient Set<Map.Entry<K,V>> entrySet;
transient Collection<V> values;
/* ---------------- Small Utilities -------------- */
构造方法
//创建一个带有默认初始容量 (16)、加载因子 (0.75) 和 concurrencyLevel (16) 的新的空映射。
ConcurrentHashMap()
// 创建一个带有指定初始容量、默认加载因子 (0.75) 和 concurrencyLevel (16) 的新的空映射。
ConcurrentHashMap(int initialCapacity)
// 创建一个带有指定初始容量、加载因子和默认 concurrencyLevel (16) 的新的空映射。
ConcurrentHashMap(int initialCapacity, float loadFactor)
//创建一个带有指定初始容量、加载因子和并发级别的新的空映射。
ConcurrentHashMap(int initialCapacity, float loadFactor, int concurrencyLevel)
//构造一个与给定映射具有相同映射关系的新映射。
ConcurrentHashMap(Map<? extends K,? extends V> m)
常用核心方法
Vput(K key, V value) :将指定键映射到此表中的指定值。键和值都不可以为 null。返回以前与 key 相关联的值,如果 key 没有映射关系,则返回 null。
public V put(K key, V value) {
if (value == null)
throw new NullPointerException();//非空校验
int hash = hash(key.hashCode());//再哈希
return segmentFor(hash).put(key, hash, value, false);
}
VputIfAbsent(K key, V value):如果指定键已经不再与某个值相关联,则将它与给定值关联。返回以前与指定键相关联的值,如果该键没有映射关系,则返回 null。
public V putIfAbsent(K key, V value) {
if (value == null)
throw new NullPointerException();
int hash = hash(key.hashCode());
return segmentFor(hash).put(key, hash, value, true);
}
final Segment segmentFor(int hash):根据哈希码计算对应的段segment。
final Segment<K,V> segmentFor(int hash) {
return segments[(hash >>> segmentShift) & segmentMask];
}
Vput(K key, int hash, V value, boolean onlyIfAbsent):Segment内部类方法。
static final class Segment<K,V> extends ReentrantLock implements Serializable {
V put(K key, int hash, V value, boolean onlyIfAbsent) {
lock();//调用父类获取锁方法
try {
int c = count;
if (c++ > threshold) // 判断是否需要扩容
rehash();
HashEntry<K,V>[] tab = table;
int index = hash & (tab.length - 1);//计算索引
HashEntry<K,V> first = tab[index];//获取到对应的链表
HashEntry<K,V> e = first;
//遍历链表,获取到对应元素
while (e != null && (e.hash != hash || !key.equals(e.key)))
e = e.next;
V oldValue;
if (e != null) {//已存在对应key-value
oldValue = e.value;
if (!onlyIfAbsent)//判断是否更新value
e.value = value;
}
else {//key首次put
oldValue = null;
++modCount;//修改次数+1
tab[index] = new HashEntry<K,V>(key, hash, first, value);
count = c; // write-volatile
}
return oldValue;
} finally {
unlock();//释放锁
}
}
}
Vremove(Object key):从此映射中移除键(及其相应的值)。
public V remove(Object key) {
int hash = hash(key.hashCode());
return segmentFor(hash).remove(key, hash, null);
}
boolean remove(Object key, Object value):只有目前将键的条目映射到给定值时,才移除该键的条目。
public boolean remove(Object key, Object value) {
int hash = hash(key.hashCode());
if (value == null)
return false;
return segmentFor(hash).remove(key, hash, value) != null;
}
Vremove(Object key, int hash, Object value):Segment内部类方法。
static final class Segment<K,V> extends ReentrantLock implements Serializable {
V remove(Object key, int hash, Object value) {
lock();//获取锁
try {
int c = count - 1;
HashEntry<K,V>[] tab = table;
int index = hash & (tab.length - 1);
HashEntry<K,V> first = tab[index];
HashEntry<K,V> e = first;
//找对应key-value
while (e != null && (e.hash != hash || !key.equals(e.key)))
e = e.next;
V oldValue = null;
if (e != null) {//有对应key-value
V v = e.value;
if (value == null || value.equals(v)) {
oldValue = v;
++modCount;//修改次数+1
HashEntry<K,V> newFirst = e.next;
//next由final修饰不可变,故移除的节点前所有元素均需要clone。
for (HashEntry<K,V> p = first; p != e; p = p.next)
newFirst = new HashEntry<K,V>(p.key, p.hash, newFirst, p.value);//
tab[index] = newFirst;
count = c; // write-volatile
}
}
return oldValue;
} finally {
unlock();
}
}
}
内部类HashEntry
由于key、value、及next均由final声明,故执行remvoe操作后,移除元素的前一个元素对应entry.next值应该变了,但由于final定义,导致不能再改变它,故将它之前的节点全都克隆一次。
static final class HashEntry<K,V> {
final K key;
final int hash;
volatile V value;
final HashEntry<K,V> next;
}
Vget(Object key):返回指定键所映射到的值,如果此映射不包含该键的映射关系,则返回 null。
public V get(Object key) {
int hash = hash(key.hashCode());
return segmentFor(hash).get(key, hash);
}
Vget(Object key, int hash) :Segment内部方法。
V get(Object key, int hash) {
if (count != 0) { // read-volatile 有key-value
HashEntry<K,V> e = getFirst(hash);//找到对应的链表,并返回头节点
//遍历链表,找到对应键值对
while (e != null) {
if (e.hash == hash && key.equals(e.key)) {
V v = e.value;
if (v != null)
return v;
return readValueUnderLock(e); // recheck
}
e = e.next;
}
}
return null;
}
HashEntry<K,V> getFirst(int hash) {
HashEntry<K,V>[] tab = table;
return tab[hash & (tab.length - 1)];
}
总结:
1、 ConcurrentHashMap采用分段锁设计(Segment),默认初始将Map分为16段即Segment[]segments数组长度16,也可理解为写线程并发度为16;
2、 SegmentextendsReentrantLock则每个分段Segment都拥有自己的ReentrantLock,减少锁竞争;
3、 如果并发度设置的过小,会带来严重的锁竞争问题;如果并发度设置的过大,原本位于同一个Segment内的访问会扩散到不同的Segment中,CPUcache命中率会下降,从而引起程序性能下降;
4、 Segment中成员变量:transientvolatileintcount;表示对应Segment中key-value元素个数,当count==0时,get操作直接返回,避免不必要的査找开销;
5、 Segment中有个重要成员变量:volatiletransientConcurrentHashMap.HashEntry[]table每个Segment中key-value都以HashEntry实例存入到该数组中,若有哈希冲突,HashEntry是个链表,以链地址法解决即将哈希地址相同的key,放入到同一个链表中;
1、 根据key的哈希码再哈希inthash=hash(key.hashCode());
2、 根据新哈希码找到对应segmentsegmentFor(hash);
3、 在对应segment中计算HashEntry的索引intindex=hash&(tab.length-1);;
4、 遍历对应HashEntry[]table的table[index];
5、 匹配HashEntry的哈希码,及key,匹配成功进行对应操作;
1、 Segment改为Node,锁住node来实现减小锁粒度;
2、 synchronize替代了ReentrantLock;
3、 利用Unsafe的CAS,实现无锁化的修改值的操作;
4、 Node中next去掉了final修饰,增加了violate修饰,避免了remove后clone问题;
5、 设计了MOVED状态当resize的中过程中线程2还在put数据,线程2会帮助resize;
6、 当链表节点大于等于8时,把链表节点包装成TreeNode放在TreeBin对象中,由TreeBin完成对红黑树的包装;
ConcurrentMap 的 JavaDoc - putIfAbsent 说: /** * If the specified key is not already associated
我有这个 ConcurrentMap ConcurrentMap companyLogo = new MapMaker() .concurrencyLevel(
我该如何实现 class CaseInsensitiveConcurrentMap implements ConcurrentMap 就像ConcurrentHashMap 一样工作除了不区分大小写地
根据ConcurrentMap Javadoc : Memory consistency effects: As with other concurrent collections, actions
我有一段代码可以由多个线程执行,该代码需要执行 I/O 绑定(bind)操作,以便初始化存储在 ConcurrentMap 中的共享资源。我需要使这段代码线程安全,并避免不必要的调用来初始化共享资源。
我有一个 ConcurrentHashMap 累积了一些应该定期发送到某处的更改(用于某种复制)。关于更改,只有任何关键事项的最新值。 所以我需要获取并删除一些快照。快照不需要是原子的,只要它的每个条
ConcurrentMap 指定putIfAbsent() 的返回值为: the previous value associated with the specified key, or null i
我有一个有趣的问题需要帮助。我已经为两个不同的条件实现了几个队列,一个基于 FIFO,另一个基于键的自然顺序(ConcurrentMap)。也就是说,您可以想象两个队列具有相同的数据,只是顺序不同。我
在 Java 中,我经常需要懒惰地获取 ConcurrentMap 的条目,仅在必要时才创建。 例如我可能有 ConcurrentMap> a = new ConcurrentHashMap<>();
我经常想要访问(并且可能添加/删除)给定 ConcurrentMap 的元素,以便一次只有一个线程可以访问任何单个键。做这个的最好方式是什么?同步 key 本身不起作用:其他线程可能通过 equal
休息服务有许多彼此不相关的客户。每个客户都可以随时开始投票过程。一旦客户添加了第一票,我就会在 中创建一个条目 ConcurrentMap> votesByCustomer; 每个投票都有 voteT
我有以下代码。即使集合本身不是线程安全的,它也会是线程安全的吗? private ConcurrentMap> proxyRoles = new ConcurrentHashMap>(); p
使用 Java 8,我在 java.util.concurrent.ConcurrentMap 上实现了一个包装器接口(interface),特别是 remove(Object key)方法。由于我需
我有一段代码可以由多个线程执行,这些线程需要执行 I/O 绑定(bind)操作以初始化存储在 ConcurrentMap 中的共享资源。我需要使此代码线程安全并避免不必要的调用来初始化共享资源。这是错
AbstractConcurrentMap是Groovy中的核心类,它用于存储在运行时添加到Groovy类中的动态属性。我正在使用 Grails 2.1.2 和 Groovy 1.8.8,但我认为所有
我正在声明一个 MultiMap,并想知道将键设置为 Integer 是否是一个好主意。使用整数键与字符串键会加快检索值的速度吗? MultiMap mmCustomerOrders = hz.get
在 Java 的 ConcurrentMap 中, 有 remove(key, expectedValue) ,这将返回以下之一: 预期值在那里,但已被删除。 预期值不存在,因此未被删除。 但我想要得
很多人将 ConcurrentMap 称为缓存。 这样做是个好主意吗: public List listTasks(final ProcessDefinition def, final boolean
我正在尝试反序列化其中包含 ConcurrentMap 的对象,但出现异常。 Caused by: java.lang.IllegalArgumentException: Can not set ja
我有一个java类来处理多线程订阅服务。通过实现 Subscribable 接口(interface),可以将任务提交到服务并定期执行。代码草图如下所示: import java.util.concu
我是一名优秀的程序员,十分优秀!