精华帖 (0) :: 良好帖 (5) :: 新手帖 (6) :: 隐藏帖 (1)
|
|
---|---|
作者 | 正文 |
发表时间:2011-02-24
最后修改:2011-02-24
昨天又看人讨论这个问题,发个帖,为新手整理思路,正本清源。 很多人面试、被面试的时候都会被反复问到这个蛋疼的问题:Vector和ArrayList区别是什么?(同理StringBuffer & StringBuilder,Hashtable & HashMap,etc.) 我想很多人都会说出这个以讹传讹了好多年的标准答案:Vector是同步的,ArrayList不是。 其实最主要的核心差别是(抛开API层面的接口关系不讲),JDK1.4之前的这些类,API里面的public函数,都加了synchronized关键字,而JAVA5之后的ArrayList之类的结构,则取消了这个同步关键字限制。
取消是必然的,因为这个“同步”,现实在并发环境中起不到作用。 为了把问题简单一点,我们来看一个这样的结构。 public class MyList { private int length = 0; public synchronized int add(Object newElement) { //做添加的操作,blablabla length++; } public synchronized int size() { return this.length; } } 好的,现在有一个MyList的实例,myList,被某线程的两个实例并发访问,线程A和线程B。 我们看看如果该线程类的实例,线程A和B都执行这样代码: public void run() { if (myList.size() == 0) { //BTW:ArrayList里面应该用isEmpty()代替 myList.add(new Element()); } }
让我们看看这个MyList类的代码是Thread Safe的吗?假设一个如下的执行顺序: 1.线程A执行myList.size(),这时候线程B也进入了myList.size(),但是只能等线程A的myList.size()执行完毕 2.线程A的myList.size()执行完毕,再没有执行add之前,线程B执行了myList.size() 3.线程A执行了add,无论如何,这时候线程B也要执行add了,因为都满足了myList.size() == 0 run()函数里面的逻辑并没有正确的被执行。
synchronized 加在非静态函数上,相当于instance作为锁,即等价于: synchronized (this) { //code... } 这个锁的粒度非常不好,即难以保证类内部的成员变量在并发下保持一致(如果有多个成员变量的话),也没法保证外面调用者的宏观逻辑是正确的,反倒降低了整体性能。 所以在JAVA5之后,JAVA把这个事情扔给了程序员,数据结构只是作为基础代码存在。
更具体的说,很多朋友都知道StringBuilder性能比StringBuffer好不少,其实作为项目整体层面来说,锁和拷贝是降低性能的罪魁祸首,尤其对于性能要求很极端的项目,应该尽量减少不必要的锁和拷贝;当然sun是提供api的,就更不能加入这样的不必要的同步代码了。
开发当中,如果一定需要锁,可以为具体的要保护的无关系的成员变量单独分配锁,这样可以保证获得最大的性能。
其实JAVA5还提供了很多用于并发的数据结构,比如ConcurrentMap的putIfAbsent就在一个比较好的粒度上给简化了程序员的代码,不会犯前面这个反例的错误。
将来,如果在scala、F#等FP语言能够普及,程序员也许不必再考虑这么多的并发问题。 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2011-02-24
引用 我想很多人都会说出这个以讹传讹了好多年的标准答案:Vector是同步的,ArrayList不是。
这有什么错吗?你的文章好像是说这个是错的一样。我认为会误导人。 引用 其实最主要的核心差别是(抛开API层面的接口关系不讲),JDK1.4之前的这些类,API里面的public函数,都加了synchronized关键字,而JAVA5之后的ArrayList之类的结构,则取消了这个同步关键字限制。
那1.4呢?有没有取消?我们谈论的不是版本的区别,而是vector和arraylist,特别是以1.4开始的。 至于线程安全的ArrayList,1.5开始添加了concurrent包,有CopyOnWriteArrayList可用。 我的观点还是:Vector和ArrayList都是基于数组实现的,其中ArrayList是非线程安全的。Vector是基于Synchroinzed实现的线程安全的ArrayList,但在插入元素时容量扩充机制和ArrayList稍有不同,并可通过传入capacityIncrement来控制容量的扩充。 |
|
返回顶楼 | |
发表时间:2011-02-25
第一例子MyList,并不能说明MyList未保证线程安全。原因是调用上的错误,MyList提供的同步是对length,进行的。
这应该使用错误的问题,并不能说明什么。 |
|
返回顶楼 | |
发表时间:2011-02-25
仅仅同步意义不大,能做到线程安全才给力
|
|
返回顶楼 | |
发表时间:2011-02-25
StringBuffer 和StringBuilder 貌似就是线程安全的差别吧
|
|
返回顶楼 | |
发表时间:2011-02-25
luciferdevil 写道 StringBuffer 和StringBuilder 貌似就是线程安全的差别吧
好像不是哦。。。 |
|
返回顶楼 | |
发表时间:2011-02-25
最后修改:2011-02-25
LS几位大哥,线程安全的概念是什么呀? 弄清楚没?
我理解的线程概念: 如果某个个类的一个对象,对于能够修改这个对象状态(如add)或者调用其他方法会影响其结果(如get)的任何方法,多线程调用任一方法时,能够保证任意时刻仅有一个线程能执行, 那么这个类就是线程安全的类。 从这个概念上来说StringBuffer, Vector都是线程安全的。 但是对于 if(!vector.contains(...)){ vector.add(..) } 这段代码在多线程情况下并不安全,但不能说:vector不是线程安全的。 因此在: 单线程下:Vector,StringBuffer的线程安全特性是额外的消耗 多线程下:Vector,StringBuffer的线程安全特性在绝大多数情况下不起作用。 因此催生了ArrayList 和StringBuider的出现,并将并发控制交给程序员。 |
|
返回顶楼 | |
发表时间:2011-02-25
其实我是就是想说:不是加了个synchronized就是线程安全了,没有任何误导。
Vector的同步完全是没必要的,只能降低性能。 线程安全的关键在于控制的的粒度。 peterwei 写道 引用 我想很多人都会说出这个以讹传讹了好多年的标准答案:Vector是同步的,ArrayList不是。
这有什么错吗?你的文章好像是说这个是错的一样。我认为会误导人。 引用 其实最主要的核心差别是(抛开API层面的接口关系不讲),JDK1.4之前的这些类,API里面的public函数,都加了synchronized关键字,而JAVA5之后的ArrayList之类的结构,则取消了这个同步关键字限制。
那1.4呢?有没有取消?我们谈论的不是版本的区别,而是vector和arraylist,特别是以1.4开始的。 至于线程安全的ArrayList,1.5开始添加了concurrent包,有CopyOnWriteArrayList可用。 我的观点还是:Vector和ArrayList都是基于数组实现的,其中ArrayList是非线程安全的。Vector是基于Synchroinzed实现的线程安全的ArrayList,但在插入元素时容量扩充机制和ArrayList稍有不同,并可通过传入capacityIncrement来控制容量的扩充。 |
|
返回顶楼 | |
发表时间:2011-02-25
ahopedog2 写道 第一例子MyList,并不能说明MyList未保证线程安全。原因是调用上的错误,MyList提供的同步是对length,进行的。
这应该使用错误的问题,并不能说明什么。 是的,那个MyList只不过是Vector,StringBuffer等类的代码的一个缩影,的确那是不合理的,是个反例。 |
|
返回顶楼 | |
发表时间:2011-02-25
NanguoCoffee 写道 LS几位大哥,线程安全的概念是什么呀? 弄清楚没?
我理解的线程概念: 如果某个个类的一个对象,对于能够修改这个对象状态(如add)或者调用其他方法会影响其结果(如get)的任何方法,多线程调用任一方法时,能够保证任意时刻仅有一个线程能执行, 那么这个类就是线程安全的类。 从这个概念上来说StringBuffer, Vector都是线程安全的。 但是对于 if(!vector.contains(...)){ vector.add(..) } 这段代码在多线程情况下并不安全,但不能说:vector不是线程安全的。 因此在: 单线程下:Vector,StringBuffer的线程安全特性是额外的消耗 多线程下:Vector,StringBuffer的线程安全特性在绝大多数情况下不起作用。 因此催生了ArrayList 和StringBuider的出现,并将并发控制交给程序员。 同意。这里可能我表达的稍微有点问题,其实对于对并发理解不够深刻的程序员来说,说vector是线程安全的可能会是一个误导;你说得对,的确在某一个确切的时间点是不存在问题的,但是实际应用中,几乎99%的时候都是: if(!vector.contains(...)){ vector.add(..) } 这种代码,所以vector等结构本身的synchronized就是画蛇添足。 其实最核心的问题是:任何时候程序员写了一个synchronized,都要清楚的知道,这个synchronized到底要保护什么? |
|
返回顶楼 | |