=为了方便编写线程安全的程序,Java提供了一些线程安全的类和并发工具,例如:同步容器、并发容器、阻塞队列等,最常见的同步容器有Vector和Hashtable。那么,同步容器的所有操作都是线程安全的吗?不知道你有没有想过这个问题。本文将深入分析这个容易被忽视的问题。问题。1、同步容器在Java中,同步容器主要包括2类:1.Vector、Stack、HashTable2.Collections类中提供的静态工厂方法创建的类。本文以相对简单的Vecotr为例。我们先来看看Vector。几个重要方法的源码:publicsynchronizedbooleanadd(Ee){modCount++;ensureCapacityHelper(elementCount+1);elementData[elementCount++]=e;returntrue;}publicsynchronizedEremove(intindex){modCount++;if(index>=elementCount)thrownewArrayIndexOutOfBoundsExcepp;EoldValue=elementData(index);intnumMoved=elementCount-index-1;if(numMoved>0)System.arraycopy(elementData,index+1,elementData,index,numMoved);elementData[--elementCount]=null;//让gcdoitsworkreturnoldValue;}publicsynchronizedEget(intindex){if(index>=elementCount)thrownewArrayIndexOutOfBoundsException(index);returnelementData(index);}可以看出Vector等同步容器的所有public方法都是同步的,也就是说我们可以使用这些单独在多线程场景下的方法很有信心,因为这些方法本身确实是线程安全的。不过请注意,上面这句话中有一个关键词:单独,因为虽然同步容器的所有方法都加锁了,但是对这些容器的复合操作并不能保证它们的线程安全。客户端需要通过主动加锁来保证。举一个简单的例子,我们定义删除Vector中最后一个元素的方法如下:,包括size()和remove(),乍一看好像没什么问题。size()方法和remove()方法都是线程安全的,所以整个deleteLast方法也应该是线程安全的。但是,如果多个线程调用此方法,remove方法可能会抛出ArrayIndexOutOfBoundsException。线程“Thread-1”中的异常java.lang.ArrayIndexOutOfBoundsException:Arrayindexoutofrange:879atjava.util.Vector.remove(Vector.java:834)atcom.hollis.Test.deleteLast(EncodeTest.java:40)atcom.hollis.Test$2.run(EncodeTest.java:28)atjava.lang.Thread.run(Thread.java:748)上面我们贴出了remove的源码,我们可以分析一下:当index>=elementCount时,会抛出ArrayIndexOutOfBoundsException,即也就是说,当当前索引值不再有效时,就会抛出这个异常。因为removeLast方法可能会被多个线程同时执行,当线程2通过index()获取到索引值10时,线程1在尝试通过remove()删除索引位置的元素之前先删除索引位置的值),那么线程一执行就会抛出异常。为了避免类似问题,可以尝试加锁:publicvoiddeleteLast(){synchronized(v){intindex=v.size()-1;v.remove(index);}}如上,我们在deleteLast中加入v锁可以保证同时不会有其他线程删除v中的元素。另外,如果下面的代码会被多个线程执行,要特别注意:for(inti=0;i
