学习Java其实好久了,需要的时候也能写出个可以跑得程序,但是对其中很多机制没有仔细研究过。最近觉得该深入学习一下,于是抱着Java Core I 开始看了。
前段时间看了Java集合,目前学的是多线程。发现多线程里确实有很多东西值得好好研究。首先同步就是一个不小的问题。今天主要对集合的同步问题进行了一下小小的测试,出现了几个我自己没弄明白的问题,希望版上高手给予解答。
多线程的同步主要是对多个线程共享一个资源时的访问控制,避免出现“乱套”现象。Java提供多种机制进行同步控制,比如锁和条件对象,synchronized关键字等。而集合经常作为这种资源被多个线程共享,而且集合框架中本身也对多线程同步进行过考虑,比如有Vector,HashTable等类。于是自己写了个简单的程序,进行下测试,到底哪种方法能有效控制集合在多线程并发中的同步。
设置非常简单的一个场景,有一个Student集合,对该集合采用迭代器进行遍历之后,又对其添加了一个元素。由于添加元素是改变集合结构的操作,所以集合如果在迭代器构造之后发生改变,就会抛出ConcurrentModificationException异常。
Student类代码,很简单:
[code=Java]
class Student{
public Student(String name, int age){
this.name = name;
this.age = age;
}
String name;
int age;
public String toString(){
return "I am "+name+" , "+age+" years old.";
}
}
[/code]
1. 创建一个修改Collection的线程,实现Runnable接口。不采取任何同步措施。
[code=Java]
class ModifyCollectionTask implements Runnable{
public ModifyCollectionTask(Collection slist){
this.slist = slist;
}
public void run(){
// 遍历学生列表,
for(Student s : slist){
System.out.println(Thread.currentThread().getName());
System.out.println(s);
}
// 向学生列表添加元素
slist.add(new Student("Katie", 30));
}
Collection slist;
}
[/code]
在Main函数里启动100个线程,
[code=Java]
public class SyncCollection {
public static void main(String[] args) {
Collection<Student> slist = new ArrayList<Student>();
slist.add(new Student("AAA",10));
slist.add(new Student("BBB",12));
slist.add(new Student("CCC",14));
slist.add(new Student("DDD",16));
slist.add(new Student("EEE",18));
for(int i=0;i<100;i++){
new Thread(new ModifyCollectionTask(slist)).start();
}
}
[/code]
很明显,没有同步控制,那么很快就抛出了ConcurrentModificationException异常
2. 由于Vector类是线程安全的动态数组,所以,将集合实现改为Vector,在线程run方法中没做任何修改
[code=Java]
// 使用Vector
List sVector = new Vector();
sVector.add(new Student("AAA",10));
sVector.add(new Student("BBB",12));
sVector.add(new Student("CCC",14));
sVector.add(new Student("DDD",16));
sVector.add(new Student("EEE",18));
for(int i=0;i<100;i++){
new Thread(new ModifyCollectionTask(sVector)).start();
}
[/code]
结果还是发生了ConcurrentModificationException异常。这让我有点怀疑Vector的同步机制。
3. 使用Collections工具类中的同步包装方法,将线程不安全ArrayList进行包装,而线程实现方法没有改动
[code=Java]
// 使用Collections工具类中的同步包装器
List slist2 = Collections.synchronizedList(new ArrayList());
slist2.add(new Student("AAA",10));
slist2.add(new Student("BBB",12));
slist2.add(new Student("CCC",14));
slist2.add(new Student("DDD",16));
slist2.add(new Student("EEE",18));
for(int i=0;i<100;i++){
new Thread(new ModifyCollectionTask(slist2)).start();
}
[/code]
结果还是发生了异常,不明白。。。
4. 下面使用synchronized关键字进行同步控制,对线程实现代码进行了修改
[code=Java]
class syncModifyListTask implements Runnable{
public syncModifyListTask(Collection slist){
this.slist = slist;
}
public void run(){
synchronized(slist){
// 遍历学生列表
for(Student s : slist){
System.out.println(Thread.currentThread().getName());
System.out.println(s);
}
// 向学生列表添加元素
slist.add(new Student("Katie", 30));
}
}
Collection slist;
}
[/code]
由于有了synchronized关键字对代码片段进行了保护,所以没有出现异常
5. 使用java.util.concurrent包中的高效的同步集合, ConcurrentLinkedQueue,线程实现代码还是用ModifyCollectionTask
[code=Java]
Collection concurrentCollection = new ConcurrentLinkedQueue();
concurrentCollection.add(new Student("AAA",10));
concurrentCollection.add(new Student("BBB",12));
concurrentCollection.add(new Student("CCC",14));
concurrentCollection.add(new Student("DDD",16));
concurrentCollection.add(new Student("EEE",18));
for(int i=0;i<100;i++){
new Thread(new ModifyCollectionTask(concurrentCollection)).start();
}
[/code]
结果也没有出现异常,证明高效的同步集合还是很给力的!
将上述的各种集合同步方法进行一遍测试之后,发现Vector和Collections中的同步包装方法都不能保证同步,我就很纳闷儿,是我的使用方法出现了问题,还是这两种集合同步本身就做的不好?
希望版上的高手给予解答,谢谢!!!