之前在公家号中问了这个问题:对付线程安详的荟萃类(譬喻Vector)的任何操纵是不是都能担保线程安详?
三天之内收到120+回覆,个中暗示不清楚的或许有10人阁下,认为可以担保线程安详的有或许70人阁下,认为不能担保线程安详的有50人阁下,这个中能给出明晰表明的有5人。
别离是:
@赵鹏: size要领和get要领,假如荟萃的长度变革了,大概抛出异常, @aold619: 去网上查了资料:“有条件的线程安详 我们在 7 月份的文件“ 并发荟萃类”中接头了有条件的线程安详。有条件的线程安详类对付单独的操纵可以是线程安详的,可是某些操纵序列大概需要外部同步。条件线程安详的最常见的例子是遍历由 Hashtable 可能 Vector 可能返回的迭代器 — 由这些类返回的 fail-fast 迭代器假定在迭代器举办遍历的时候底层荟萃不会有变革。为了担保其他线程不会在遍历的时候改变荟萃,举办迭代的线程应该确保它是独有性地会见荟萃以实现遍历的完整性。凡是,独有性的会见是由对锁的同步担保的 — 而且类的文档应该说明是哪个锁(凡是是工具的内部监督器(intrinsic monitor))。 假如对一个有条件线程安详类举办记录,那么您应该不只要记录它是有条件线程安详的,并且还要记录必需防备哪些操纵序列的并发会见。用户可以公道地假设其他操纵序列不需要任何特另外同步。” @闫晓琦: 答,不是,常常会呈现数组越界报错 @逆风飞扬: vector单个的要领 synchronized并不代表vector组合的要领挪用具有原子性。有组合的操纵照旧需要针对vector举办加锁。 @慕容: 不是,线程安详荟萃只能担保单个操纵安详,复合操纵同样不安详
那么这个问题的正解应该是什么的。
问:对付线程安详的荟萃类(譬喻Vector)的任何操纵是不是都能担保线程安详?
答:同步容器中的所有自带要领都是线程安详的,因为要领都利用synchronized要害字标注。可是,对这些荟萃类的复合操纵无法担保其线程安详性。需要客户端通过主动加锁来担保
假如你看过JDK的源码,那么你会发明,像Vector这样的同步容器的所有共有要领全都是synchronized的。也就是说,我们可以在多线程场景中安心的利用单独这些要领,因为这些要领自己简直是线程安详的。那么为什么又说复合操纵无法担保线程安详呢?这里举个栗子,我们界说如下删除Vector中最后一个元素要领:
public Object deleteLast(Vector v){ int lastIndex = v.size()-1; v.remove(lastIndex); }
上面这个要领是一个复合要领,包罗size()和remove(),劳务派遣管理系统,乍一看上去仿佛并没有什么问题,无论是size()要领照旧remove()要领都是线程安详的,那么整个deleteLast要领应该也是线程安详的。可是时,假如多线程挪用该要领的进程中有,remove要领有大概抛出ArrayIndexOutOfBoundsException。我们看一下remove要领详细实现,什么环境下会抛出这个异常呢。
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; }
从上面代码中可以看出,当index >= elementCount时,劳务派遣管理系统,会抛出ArrayIndexOutOfBoundsException,也就是说,当当前索引值不再有效的时候,软件开发,将会抛出这个异常。因为removeLast要领,有大概被多个线程同时执行,当线程一通过index()得到索引值为10,在实验通过remove()删除该索引位置的元素之前,线程2把该索引位置的值删除去了,这时线程一在执行时便会抛出异常。
为了制止呈现雷同问题,可以实验加锁:
public void deleteLast() { synchronized (v) { int index = v.size() - 1; v.remove(index); } }
如上,我们在deleteLast中,对v举办加锁,即可担保同一时刻,不会有其他线程删除去v中的元素。
至此,我们已经表明清楚了我们的问题。
问:对付线程安详的荟萃类(譬喻Vector)的任何操纵是不是都能担保线程安详?
答:同步容器中的所有自带要领都是线程安详的,因为要领都利用synchronized要害字标注。可是,对这些荟萃类的复合操纵无法担保其线程安详性。需要客户端通过主动加锁来担保。
由于我们本身已知Vector等同步容器是线程安详的,所以我们凡是在多线程场景中会直接拿来利用,并不会思量太多,从而大概导致问题。