并发下的ArrayList
我们都知道,ArrayList是一个线程不安全的容器。如果在多线程中使用ArrayList,可能会导致程序出错。但是问题出现在哪里呢?看一下下边的代码:
public class ArrayListMultiThread { static Vector<Integer> al = new Vector<Integer>(); public static class AddThread implements Runnable { @Override public void run() { // TODO Auto-generated method stub for (int i = 0; i < 1000000; i++) { al.add(i); } } } public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(new AddThread()); Thread t2 = new Thread(new AddThread()); t1.start();t2.start(); t1.join();t2.join(); System.out.println(al.size()); } }
代码中t1和t2两个线程同时向一个ArrayList容器中添加数据。因此我们期待最后可以有2000000个元素在ArrayList中,如果你执行这段代码,你可能会得到三种结果。
- 程序正常结束,ArrayList的大小最终确实是2000000.这说明并行程序有问题,也未必会每次都表现出来。
- 抛出异常:这是因为ArrayList在扩容过程中,内部一致性被破坏,但由于没有锁的保护,另一个线程访问到了不一致的内容状态,导致出现越界问题。
Exception in thread "Thread-0" java.lang.ArrayIndexOutOfBoundsException: 10 at java.util.ArrayList.add(ArrayList.java:459) at com.test.thread.ArrayListMultiThread$AddThread.run(ArrayListMultiThread.java:16) at java.lang.Thread.run(Thread.java:748) 1000009
3.出现了一个非常隐蔽的错误,比如打印小于2000000的值显然,这是由于多线程访问冲突,使的保存容器大小的变量被多线程不正常的访问,同时两个线程同时对ArrayList中的同一个位置星星赋值导致的。
改进的方法很简单,使用线程安全的Vector代替ArrayList即可。