java 集合读写同步_JAVA多线程学习十六 - 同步集合类的应用
1.引言
在多線程的環(huán)境中,如果想要使用容器類(lèi),就需要注意所使用的容器類(lèi)是否是線程安全的。在最早開(kāi)始,人們一般都在使用同步容器(Vector,HashTable),其基本的原理,就是針對(duì)容器的每一個(gè)操作,都添加synchronized來(lái)進(jìn)行同步,此種方式盡管簡(jiǎn)單,但是其性能是非常地下的,所以現(xiàn)在已經(jīng)不怎么使用了。人們普遍會(huì)使用并發(fā)的容器,在JDK1.5之后,針對(duì)基于散列的Map,提供了新的ConcurrentHashMap,針對(duì)迭代需求的list,提供了CopyOnWriteList.
2.ConcurrentHashMap
ConcurrentHashMap使用了一種分段鎖的策略,使得map可以被多個(gè)讀寫(xiě)線程并行的訪問(wèn)。基本可以認(rèn)為是將map的key值范圍分為多個(gè)段,這樣多個(gè)線程訪問(wèn)的時(shí)候,他們需要訪問(wèn)的key值在不同的段,所以可以互相不干擾,
使用不同的鎖對(duì)象來(lái)進(jìn)行并發(fā)操作。
ConcurrentHashMap在使用迭代器遍歷的時(shí)候,不會(huì)報(bào)ConcurrentModificationException,提供“弱一致性”。在遍歷迭代的時(shí)候,也會(huì)反應(yīng)出在迭代器創(chuàng)建之后的數(shù)據(jù)修改。
應(yīng)用場(chǎng)景
針對(duì)一般的有并發(fā)需求的map,都應(yīng)該使用ConcurrentHashMap. 它的性能優(yōu)于Hashtable和synchronizedMap。
缺點(diǎn)
不是強(qiáng)一致性
由于是采用的分段鎖策略,所以一些數(shù)據(jù)不能保證強(qiáng)一致性。比如針對(duì)容器的size方法,由于線程A只是獲得了自己的分段鎖,它不能保證其他線程對(duì)容器的修改,所以此時(shí)線程A可能使用size,會(huì)得到不穩(wěn)定數(shù)據(jù)。這種情況下,是對(duì)同步性能的一些折衷。如果業(yè)務(wù)需求必須滿足強(qiáng)一致性,才會(huì)需要對(duì)整個(gè)Map進(jìn)行鎖操作。并發(fā)容器的弱一致性的概念背景,是在高并發(fā)情況下,容器的size和isEmpty之類(lèi)的方法,用處不大,所以可以忍受數(shù)據(jù)不一致性。
3.CopyOnWrite容器
在JDK1.5之后,java.util.concurrent引入了兩個(gè)CopyOnWrite容器,分別是CopyOnWriteArrayList, CopyOnWriteArraySet.
顧名思義,CopyOnWrite就是在write操作之前,對(duì)集合進(jìn)行Copy,針對(duì)容器的任意改操作(add,set,remove之類(lèi)),都是在容器的副本上進(jìn)行的。并且在修改完之后,將原容器的引用指向修改后的副本。
如果線程A得到容器list1的iterator之后,線程B對(duì)容器list1加入了新的元素,由于線程A獲得list1的iterator時(shí)候在線程B對(duì)list1進(jìn)行修改前,所以線程A是看不到線程B對(duì)list1進(jìn)行的任何修改。
具體到源碼,看一下add操作
/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return {@code true} (as specified by {@link Collection#add})
*/
public boolean add(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
可以發(fā)現(xiàn),寫(xiě)操作是會(huì)有個(gè)鎖lock.lock(),這保證了多線程寫(xiě)操作之間的同步。之后使用Arrays.copyOf來(lái)進(jìn)行數(shù)組拷貝,在修改完成后,setArray(newElements)將原來(lái)的數(shù)組引用指向新的數(shù)組。
應(yīng)用場(chǎng)景
經(jīng)常用在讀多寫(xiě)少的場(chǎng)景,比如EventListener的添加,網(wǎng)站的category列表等偶爾修改,但是需要大量讀取的情景。
缺點(diǎn)
1.數(shù)據(jù)一致性的問(wèn)題。
因?yàn)樽x操作沒(méi)有用到并發(fā)控制,所以可能某個(gè)線程讀到的數(shù)據(jù)不是實(shí)時(shí)數(shù)據(jù)。
2.內(nèi)存占用問(wèn)題。
因?yàn)閷?xiě)操作會(huì)進(jìn)行數(shù)據(jù)拷貝,并且舊有的數(shù)據(jù)引用也可能被其他線程占有一段時(shí)間,這樣針對(duì)數(shù)據(jù)比較大的情況,可能會(huì)占用相當(dāng)大的內(nèi)存。并且由于每次寫(xiě)操作都會(huì)占用額外的內(nèi)存,最后進(jìn)行的GC時(shí)間也可能相應(yīng)的增加。
4.HashSet
HashSet內(nèi)部是用的HashMap,只用了HashMap的key。
同步集合
傳統(tǒng)集合類(lèi)在并發(fā)訪問(wèn)時(shí)的問(wèn)題說(shuō)明:死鎖死循環(huán)
傳統(tǒng)方式下用Collections工具類(lèi)提供的synchronizedCollection方法來(lái)獲得同步集合,分析該方法的實(shí)現(xiàn)源碼
Java5中提供了如下一些同步集合類(lèi):
通過(guò)看java.util.concurrent包下的介紹可以知道有哪些并發(fā)集合
ConcurrentHashMap
CopyOnWriteArrayList
CopyOnWriteArraySet
傳統(tǒng)方式下的Conllection在迭代結(jié)合時(shí),不允許對(duì)集合進(jìn)行修改。
根據(jù)AbstractList的checkForComodification方法的源碼,分析產(chǎn)生ConcurrentModificationException異常的原因。
public class User implements Cloneable{
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public boolean equals(Object obj) {
if(this == obj) {
return true;
}
if(!(obj instanceof User)) {
return false;
}
User user = (User)obj;
//if(this.name==user.name && this.age==user.age)
if(this.name.equals(user.name)
&& this.age==user.age) {
return true;
}
else {
return false;
}
}
public int hashCode() {
return name.hashCode() + age;
}
public String toString() {
return "{name:'" + name + "',age:" + age + "}";
}
public Object clone() {
Object object = null;
try {
object = super.clone();
} catch (CloneNotSupportedException e) {}
return object;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
}
總結(jié)
以上是生活随笔為你收集整理的java 集合读写同步_JAVA多线程学习十六 - 同步集合类的应用的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: python数据库实例_Python3.
- 下一篇: java避免使用orderby_java