程序猿们大多都知道,Vector是ArrayList的多线程版本,HashTable是HashMap的多线程版本,Vector和HashTable都是线程安全的。对吧?
下面,我来写一个Vector的例子,验证下多线程的问题。
public class VectorTest {
public static void main(String[] args) {
final List<String> apples = new Vector<String>();
for(int i=0; i<10000; i++){
apples.add("apple"+i);
}
//add apples
Thread addThread = new Thread(){
@Override
public void run(){
while(true){
apples.add("apple"+new Random().nextInt());
}
}
};
Thread eatThread = new Thread(){
@Override
public void run(){
for(String apple : apples){
apples.remove(apple);
}
}
};
addThread.start();
eatThread.start();
}
}
这段代码,到底能否正常运行呢?Vector是线程安全的,现在2个线程,1个add操作,1个remove操作,应该没问题的吧。
但事实是,抛出ConcurrentModificationException。Vector不是线程安全的吗?怎么现在2个线程同时访问Vector,怎么就出错了呢?
其实,是你搞错概念了。Vector是线程安全,但现在抛出的是同步修改异常。所有的集合类都有1个Fail-Fast的校验机制,当一个集合在被多个线程修改并访问时,就可能出现异常--ConcurrentModificationException。线程同步是为了保护集合中的数据不被脏读、写而设置的。
下面,让我们来写1个线程安全的正确例子:
public class VectorTest2 {
public static void main(String[] args) {
final List<String> apples = new Vector<String>();
for(int i=0; i<100; i++){
apples.add("apple"+i);
}
System.out.println("apples' size = "+apples.size());
for(int i = 0; i < 10; i++){
Thread eatThread = new Thread(){
@Override
public void run(){
while(apples.size() > 0){
System.out.println(Thread.currentThread().getId()+" eat Apple "+apples.remove(0));
}
}
};
eatThread.start();
}
}
}
现在,我们先初始化100个苹果,然后让10个人开始吃,10个吃完程序就结束了。
注意:我们说Vector, HashTable是线程安全的,指的是多个线程同时操作1个方法时,是安全的。而不是操作多个方法,操作多个方法那叫线程同步了。Vector, HashTable是线程安全的,但线程同步是需要自己去实现的。