一般面试都有一些线程题目,大致是某某集合和某某集合什么区别,比如Hashmap .HashTable,ConcurrentHashMap有什么区别
很多网上答案都说线程安全,一个线程不安全。那么这里不是讨论什么叫线程安全,只是讨论线程不安全怎么办。
以HashMap为例,它不安全,什么叫不安全。简单理解就是多线程读写可能是脏数据,比如你在A线程读,B线程写了一个新数据,而
A线程却不知道。可怕。。
之前有一个奔溃是java.util.ConcurrentModificationException,就是线程不安全的操作,不过更专业的说法是快速失败。HashMap在读的
过程中,如果写操作,jdk在处理这种情况是有一个判断的:
即迭代器迭代,检测当前集合大小,如果大小不符合预期则抛出异常达到快速失败的效果。
例如迭代器迭代开始时候是4个key,中间发现变成5个key,这时候迭代器直接失败抛出异常,因为
它认为迭代中是读操作,至始至终都应该是不变的集合才能保障效果。
这里有一些问题需要解释:
1.迭代过程,修改key对应的键值对,那也是修改了,那迭代要不要抛异常。如果抛异常怎么判断?毕竟集合大小没有变化
开启阅读源码模式
-----会奔溃的代码。
public static void main(String args[]) {
HashMap<String, String> map = new HashMap();
map.put("0", "1");
map.put("1", "x");
map.put("2", "x");
Iterator<String> i = map.keySet().iterator();
while (i.hasNext()) {
if (map.get(i.next()).equals("x")) {
map.put("ok", "true");
}
}
}
-------结束,那么问题应该是i.hasNext()
------jump it.read the fuck code
i是hashmap的键对应迭代器,简单介绍一下背景,这里是HashMap$KeySet,对应就是这里的内部类实现的迭代器
private final class KeySet extends AbstractSet<K> {
public Iterator<K> iterator() {
return newKeyIterator();
}
||
V
Iterator<K> newKeyIterator() {
return new KeyIterator();
}
||
V
原来就是KeyIterator类了。
private final class KeyIterator extends HashIterator<K> {
public K next() {
return nextEntry().getKey();
}
}
||
V
final Entry<K,V> nextEntry() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
Entry<K,V> e = next;
if (e == null)
throw new NoSuchElementException();
if ((next = e.next) == null) {
Entry[] t = table;
while (index < t.length && (next = t[index++]) == null)
;
}
current = e;
return e;
}
-----关键代码就是第一步的判断,也就是希望值和实际值不符合直接抛异常,这不就是生活中不符合预期直接解雇你吗。哈哈哈哈哈哈h
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
小情绪梳理一下,HashMap迭代器中,每一次迭代都判断当前梳理和预期梳理是不是符合保持一致,如果不一致不再迭代直接抛异常。
那么再解释一下几个变量的原理
modCount != expectedModCount
/**
* The number of times this HashMap has been structurally modified
* Structural modifications are those that change the number of mappings in
* the HashMap or otherwise modify its internal structure (e.g.,
* rehash). This field is used to make iterators on Collection-views of
* the HashMap fail-fast. (See ConcurrentModificationException).
*/
transient int modCount;
HashMap结构修改的次数,包括那些修改Hashmap映射的数量或者导致内部结构
变化的数量,这个变量是用于快速失败
就是这么理解呗,hashmap变化就将变量+1
常见的是不是有put操作,remove操作,
看看