面试官问我同步容器(如Vector)的所有操作一定是线程安全的吗?我懵了!
作者:媒体转发 时间:2019-08-29 01:36
=

为了方便编写出线程安全的程序,Java里面提供了一些线程安全类和并发工具,比如:同步容器、并发容器、阻塞队列等。
最常见的同步容器就是Vector和Hashtable了,那么,同步容器的所有操作都是线程安全的吗?
这个问题不知道你有没有想过,本文就来深入分析一下这个问题,一个很容易被忽略的问题。
1.同步容器
在Java中,同步容器主要包括2类:
1、Vector、Stack、HashTable
2、Collections类中提供的静态工厂方法创建的类
本文拿相对简单的Vecotr来举例,我们先来看下Vector中几个重要方法的源码:
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
public synchronized E remove(int index) {
modCount++;
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
E oldValue = elementData(index);
int numMoved = elementCount - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--elementCount] = null; // Let gc do its work
return oldValue;
}
public synchronized E get(int index) {
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
return elementData(index);
}
可以看到,Vector这样的同步容器的所有公有方法全都是synchronized的,也就是说,我们可以在多线程场景中放心的使用单独这些方法,因为这些方法本身的确是线程安全的。但是,请注意上面这句话中,有一个比较关键的词:单独因为,虽然同步容器的所有方法都加了锁,但是对这些容器的复合操作无法保证其线程安全性。需要客户端通过主动加锁来保证。简单举一个例子,我们定义如下删除Vector中最后一个元素方法:
public Object deleteLast(Vector v){
int lastIndex = v.size()-1;
v.remove(lastIndex);
}
上面这个方法是一个复合方法,包括size()和remove(),乍一看上去好像并没有什么问题,无论是size()方法还是remove()方法都是线程安全的,那么整个deleteLast方法应该也是线程安全的。但是时,如果多线程调用该方法的过程中,remove方法有可能抛出ArrayIndexOutOfBoundsException。
Exception in thread "Thread-1" java.lang.ArrayIndexOutOfBoundsException: Array index out of range: 879
at java.util.Vector.remove(Vector.java:834)
at com.hollis.Test.deleteLast(EncodeTest.java:40)
at com.hollis.Test$2.run(EncodeTest.java:28)
at java.lang.Thread.run(Thread.java:748)



