Java集合类中某个线程在 Collection 上进行迭代时通常不允许另一个线性修改该 Collection通常在这些情况下迭代的结果是不确定的如果检测到这种行为一些迭代器实现(包括 JRE 提供的所有通用 collection 实现)可能选择抛出此异常执行该操作的迭代器称为快速失败 迭代器因为迭代器很快就完全失败而不会冒着在将来某个时间任意发生不确定行为的风险
因此当一个线程试图ArrayList的数据的时候另一个线程对ArrayList在进行迭代的会出错抛出ConcurrentModificationException
比如下面的代码
final List<String> tickets = new ArrayList<String>();
for (int i = ; i < ; i++) {
ticketsadd(ticket NO + i);
}
Systemoutprintln(start);
for (int i = ; i < ; i++) {
Thread salethread = new Thread() {
public void run() {
while (ticketssize() > ) {
ticketsremove();
Systemoutprintln(ThreadcurrentThread()getId()+Remove );
}
}
};
salethreadstart();
}
Systemoutprintln(start);
new Thread() {
public void run() {
for (String s : tickets) {
Systemoutprintln(s);
}
}
}start();
上述程序运行后会在某处抛出异常
javautilConcurrentModificationException
at javautilArrayList$ItrcheckForComodification(Unknown Source)
at javautilArrayList$Itrnext(Unknown Source)
at mytestmytestpkgTj$run(Tjjava:)
Vector是线程同步的那么把ArrayList改成Vector是不是就对了呢?
答案是否定的事实上无论是ArrayList还是Vector只要是实现Collection接口的都要遵循failfast的检测机制即在迭代是时候不能修改集合的元素一旦发现违法这个规定就会抛出异常
事实上Vector相对于ArrayList的线程同步体现在对集合元素是否髒读上即ArrayList允许髒读而Vector特殊的机制不会出现髒读但是效率会很差
举个例子一个集合有个线程从该集合中删除元素那么每个元素只可能由一个线程删除掉不可能会出现一个元素被多个线程删除的情况
比如下面的代码
final List<String> tickets = new ArrayList<String>();
for (int i = ; i < ; i++) {
ticketsadd(ticket NO + i);
}
Systemoutprintln(start);
for (int i = ; i < ; i++) {
Thread salethread = new Thread() {
public void run() {
while (true) {
if(ticketssize()>)
Systemoutprintln(ThreadcurrentThread()getId()+ ticketsremove());
else
break;
}
}
};
salethreadstart();
}
for循环构造个线程删除同一个集合中的数据理论上只能删除次但是运行完发现输出的删除次数次其中很多数据都是被多个线程删除比如下面的输出片段
ticket NO
ticket NO
ticket NO
ticket NO
ticket NO
ticket NO
ticket NO
ticket NO
ticket NO
ticket NO
ticket NO
可以看到都被多个线程删除这事实上就是出现了髒读解决的办法就是加锁使得同一时刻只有个线程对ArrayList做操作
修改代码synchronized关键字让得到锁对象的线程才能运行这样确保同一时刻只有一个线程操作集合
final List<String> tickets = new ArrayList<String>();
for (int i = ; i < ; i++) {
ticketsadd(ticket NO + i);
}
Systemoutprintln(start);
final Object lock=new Object();
for (int i = ; i < ; i++) {
Thread salethread = new Thread() {
public void run() {
while (true) {
synchronized(lock)
{
if(ticketssize()>)
Systemoutprintln(ThreadcurrentThread()getId()+ ticketsremove());
else
break;
}
}
}
};
salethreadstart();
}
这样得到的结果就是准确的了
当然不使用synchronized关键字而直接使用vector或者CollectionssynchronizedList 也是同样效果
final List<String> tickets =javautilCollectionssynchronizedList(new ArrayList<String>());
final List<String> tickets =new Vector<String>();
vector和CollectionssynchronizedList 都是线程同步的避免的髒读的出现