该帖已经被评为良好帖
|
|
---|---|
作者 | 正文 |
发表时间:2009-03-10
marlonyao 写道 dlovek 写道 恩,我有看到在Segment的删除/添加方法都是先修改了modCount然后修改了volatile的count值。
在containsValue方法中,“内层的第一个for循环,里面有语句int c = segments[i].count; 但是c却从来没有被使用过,即使如此,编译器也不能做优化将这条语句去掉,因为存在对volatile变量count的读取,这条语句存在的唯一目的就是保证segments[i].modCount读取到几乎最新的值”,这个就不理解了。 见我前面的回复,如果不先读count,就有可能读到modCount很久以前的陈旧值。 volatile 不能100%保证读到的是最新的数值,也可说volatile是轻量级别的sychronized。 |
|
返回顶楼 | |
发表时间:2009-03-10
看完文章后有个感觉 ConcurrentHashMap适合在高读并发低写并发的情况下使用 如果在高写并发的情况下使用 连get的安全性都不能保障 是这个意思吗? 写并发的负载到什么程度ConcurrentHashMap才会不安全?
|
|
返回顶楼 | |
发表时间:2009-03-10
unsid 写道 final Segment<K,V>[] segments;
final Segment<K,V> segmentFor(int hash) { return segments[(hash >>> segmentShift) & segmentMask]; } public V remove(Object key) { hash = hash(key.hashCode()); return segmentFor(hash).remove(key, hash, null); } 我对此处的segments不理解,这里的segments数组是干吗的?我起初还以为相当于HashMap中的Entity[],仔细一看,每个segment里面都有一个HashEntry[],这个应当是真正哈西表按照楼主说的,每个用户操作在不同的segment上不同segment有不同的锁,那让segment[]充当Entity[],而每个segment里仅有一个HashEntry链表,这样只有两个用户删除哈西值相同的元素时才需要同步. 为虾米先要segmentFor(hash)而后在remove方法里又哈西算一次坐标? int index = hash & (tab.length - 1); HashEntry<K,V> first = tab[index]; 每个Segment包含多个hash链。 |
|
返回顶楼 | |
发表时间:2009-03-10
mercyblitz 写道 marlonyao 写道 dlovek 写道 恩,我有看到在Segment的删除/添加方法都是先修改了modCount然后修改了volatile的count值。
在containsValue方法中,“内层的第一个for循环,里面有语句int c = segments[i].count; 但是c却从来没有被使用过,即使如此,编译器也不能做优化将这条语句去掉,因为存在对volatile变量count的读取,这条语句存在的唯一目的就是保证segments[i].modCount读取到几乎最新的值”,这个就不理解了。 见我前面的回复,如果不先读count,就有可能读到modCount很久以前的陈旧值。 volatile 不能100%保证读到的是最新的数值,也可说volatile是轻量级别的sychronized。 volatile保证100%读取到最新的数据,对这里来说,它保证100%读取到count的最新值,但是对非volatile变量就不一样了。 |
|
返回顶楼 | |
发表时间:2009-03-10
kevinwong 写道 看完文章后有个感觉 ConcurrentHashMap适合在高读并发低写并发的情况下使用 如果在高写并发的情况下使用 连get的安全性都不能保障 是这个意思吗? 写并发的负载到什么程度ConcurrentHashMap才会不安全?
get总是安全的。 |
|
返回顶楼 | |
发表时间:2009-03-10
marlonyao 写道 kevinwong 写道 看完文章后有个感觉 ConcurrentHashMap适合在高读并发低写并发的情况下使用 如果在高写并发的情况下使用 连get的安全性都不能保障 是这个意思吗? 写并发的负载到什么程度ConcurrentHashMap才会不安全?
get总是安全的。 引用 get操作不需要锁。第一步是访问count变量,这是一个volatile变量,由于所有的修改操作在进行结构修改时都会在最后一步写count变量,通过这种机制保证get操作能够得到几乎最新的结构更新。对于非结构更新,也就是结点值的改变,由于HashEntry的value变量是volatile的,也能保证读取到最新的值。接下来就是对hash链进行遍历找到要获取的结点,如果没有找到,直接访回null。对hash链进行遍历不需要加锁的原因在于链指针next是final的。但是头指针却不是final的,这是通过getFirst(hash)方法返回,也就是存在table数组中的值。这使得getFirst(hash)可能返回过时的头结点,例如,当执行get方法时,刚执行完getFirst(hash)之后,另一个线程执行了删除操作并更新头结点,这就导致get方法中返回的头结点不是最新的。这是可以允许,通过对count变量的协调机制,get能读取到几乎最新的数据,虽然可能不是最新的。要得到最新的数据,只有采用完全的同步。
这是原话 而且新put的entity都是放到节点头的 那说明在一个线程内put的entity在另一个线程内不保证能get出来 这是我的理解 |
|
返回顶楼 | |
发表时间:2009-03-10
kevinwong 写道 marlonyao 写道kevinwong 写道看完文章后有个感觉 ConcurrentHashMap适合在高读并发低写并发的情况下使用 如果在高写并发的情况下使用 连get的安全性都不能保障 是这个意思吗? 写并发的负载到什么程度ConcurrentHashMap才会不安全? get总是安全的。 引用 get操作不需要锁。第一步是访问count变量,这是一个volatile变量,由于所有的修改操作在进行结构修改时都会在最后一步写count变量,通过这种机制保证get操作能够得到几乎最新的结构更新。对于非结构更新,也就是结点值的改变,由于HashEntry的value变量是volatile的,也能保证读取到最新的值。接下来就是对hash链进行遍历找到要获取的结点,如果没有找到,直接访回null。对hash链进行遍历不需要加锁的原因在于链指针next是final的。但是头指针却不是final的,这是通过getFirst(hash)方法返回,也就是存在table数组中的值。这使得getFirst(hash)可能返回过时的头结点,例如,当执行get方法时,刚执行完getFirst(hash)之后,另一个线程执行了删除操作并更新头结点,这就导致get方法中返回的头结点不是最新的。这是可以允许,通过对count变量的协调机制,get能读取到几乎最新的数据,虽然可能不是最新的。要得到最新的数据,只有采用完全的同步。 这是原话 而且新put的entity都是放到节点头的 那说明在一个线程内put的entity在另一个线程内不保证能get出来 这是我的理解 是的。但是get能够看到调用get方法之前所有已经完成的put(因为如果发生结构性改变它会写count),虽然可能看不到正在进行的put操作(还没有写count)。这是一个很小的时间窗口,这不可避免,因为get没有同步。这并不是说get不安全,即使get操作时,其它线程在put,get也能看到一致的hash链(与HashMap相比较)。在HashMap中,假设put方法是同步的,get没有同步,get有可能看到实际上从不存在hash链。 get方法总是返回最近put过的数据,这句话说得很别扭,却是很重要的。假设其它线程先后调用了put("a", 1),put("a", 2),put("a", 3),这里能够使用“先后”,是因为put方法是同步的,put方法是排序的,每一个时刻只能够发生一个put方法。接着一个线程调用get("a")方法,并且另一个线程正在调用put("a", 4)方法,那么get("a")会返回3或者4,完全取决于("a")访问结点的值(读value)和put("a", 4)写结点的值(写value)哪个先发生(读volatile变量和写volatile总有个先后顺序,读写普通变量就不一定了,它们可能同时发生),这时put不会发生结构性更新。如果在调用get("a")时,另一个线程正在调用remove("a"),get方法要么返回3,要么返回null,这取决于get方法中getFirst(hash)返回的到底是remove("a")之前还是之后的头结点,而绝不会返回1, 2或任何其它的值。 |
|
返回顶楼 | |
发表时间:2009-03-11
marlonyao 写道 kevinwong 写道 marlonyao 写道kevinwong 写道看完文章后有个感觉 ConcurrentHashMap适合在高读并发低写并发的情况下使用 如果在高写并发的情况下使用 连get的安全性都不能保障 是这个意思吗? 写并发的负载到什么程度ConcurrentHashMap才会不安全? get总是安全的。 引用 get操作不需要锁。第一步是访问count变量,这是一个volatile变量,由于所有的修改操作在进行结构修改时都会在最后一步写count变量,通过这种机制保证get操作能够得到几乎最新的结构更新。对于非结构更新,也就是结点值的改变,由于HashEntry的value变量是volatile的,也能保证读取到最新的值。接下来就是对hash链进行遍历找到要获取的结点,如果没有找到,直接访回null。对hash链进行遍历不需要加锁的原因在于链指针next是final的。但是头指针却不是final的,这是通过getFirst(hash)方法返回,也就是存在table数组中的值。这使得getFirst(hash)可能返回过时的头结点,例如,当执行get方法时,刚执行完getFirst(hash)之后,另一个线程执行了删除操作并更新头结点,这就导致get方法中返回的头结点不是最新的。这是可以允许,通过对count变量的协调机制,get能读取到几乎最新的数据,虽然可能不是最新的。要得到最新的数据,只有采用完全的同步。 这是原话 而且新put的entity都是放到节点头的 那说明在一个线程内put的entity在另一个线程内不保证能get出来 这是我的理解 是的。但是get能够看到调用get方法之前所有已经完成的put(因为如果发生结构性改变它会写count),虽然可能看不到正在进行的put操作(还没有写count)。这是一个很小的时间窗口,这不可避免,因为get没有同步。这并不是说get不安全,即使get操作时,其它线程在put,get也能看到一致的hash链(与HashMap相比较)。在HashMap中,假设put方法是同步的,get没有同步,get有可能看到实际上从不存在hash链。 get方法总是返回最近put过的数据,这句话说得很别扭,却是很重要的。假设其它线程先后调用了put("a", 1),put("a", 2),put("a", 3),这里能够使用“先后”,是因为put方法是同步的,put方法是排序的,每一个时刻只能够发生一个put方法。接着一个线程调用get("a")方法,并且另一个线程正在调用put("a", 4)方法,那么get("a")会返回3或者4,完全取决于("a")访问结点的值(读value)和put("a", 4)写结点的值(写value)哪个先发生(读volatile变量和写volatile总有个先后顺序,读写普通变量就不一定了,它们可能同时发生),这时put不会发生结构性更新。如果在调用get("a")时,另一个线程正在调用remove("a"),get方法要么返回3,要么返回null,这取决于get方法中getFirst(hash)返回的到底是remove("a")之前还是之后的头结点,而绝不会返回1, 2或任何其它的值。 受教了 谢谢:) |
|
返回顶楼 | |
发表时间:2009-03-11
最后修改:2009-03-11
原来的HashTable一锁就锁掉了整个map,现在这个锁只锁了一个segments,锁的范围小了,很多情况下可以不所解决问题。。。于是效率提高了。。。
|
|
返回顶楼 | |
发表时间:2009-03-11
为什么删除的时候要吧原来的节点复制一次,不能直接删除吗?
|
|
返回顶楼 | |