Vector 真的线程安全的吗?

JAVA herman 51浏览

你还在面试中回答 Vector 是线程安全的吗?我相信很多人在面试中都是这样回答的,它是线程安全的。关于 Vector 我们今天来重新认识它!

如果你说 Vector 是线程安全的,一定是基于它的方法 synchronized 关键字的所影响的,客观的认为它是线程安全的!

但实际上 Vector 在复合操作下,它并不是一个线程安全的集合!

Vector 集合

什么?怎么可能,加了 synchronized 关键字还不是线程安全的,小编你一定是在骗我对不对?

说它不是线程安全的,是在同步容器和并发容器的角度来说的。

光说不练是假把式!我们一起来看一段代码:

public static Object getLast(Vector list) {  
    int lastIndex = list.size() - 1;  
    return list.get(lastIndex);  
} 
public static void deleteLast(Vector list) {  
    int lastIndex = list.size() - 1;  
    list.remove(lastIndex);  
} 

上面的代码取最后一个元素或者删除最后一个元素,使用了同步容器Vector。如果有两个线程A,B同时调用上面的两个方法,假设list的大小为10,这里计算得到的lastIndex为9,线程B首先执行了删除操作(多线程之间操作执行的不确定性导致),而后线程A调用了list.get方法,这时就会发生数组越界异常,导致问题的原因就是上面的复合操作不是原子操作,这里可以通过在方法内部额外的使用list对象锁来实现原子操作。

如果还没看懂,我们再继续看一个例子:

if (!vector.contains(element)) 
	vector.add(element); 
	...
	// 业余草:www.xttblog.com
}

明白了吧!

这是经典的 put-if-absent 情况,尽管 contains, add 方法都正确地同步了,但作为 vector 之外的使用环境,仍然存在 race condition(锁竞争)。

虽然条件判断contains与add都是原子性的操作 (atomic),但在 if 条件判断为真后,那个用来访问vector.contains 方法的锁已经释放,在即将的 vector.add 方法调用 之间有间隙,在多线程环境中,完全有可能被其他线程获得 vector的 lock 并改变其状态, 此时当前线程的vector.add(element); 正在等待(只不过我们不知道而已)。只有当其他线程释放了 vector 的 lock 后,vector.add(element); 继续,但此时它已经基于一个错误的假设了。

单个的方法 synchronized 了并不代表组合(compound)的方法调用具有原子性,使 compound actions 成为线程安全的可能解决办法之一还是离不开intrinsic lock (这个锁应该是 vector 的,但由 client 维护)。

所以,对于文章开头的那个那个“愚蠢”的问题,正确地回答应该是: Vector 和 ArrayList 实现了同一接口 List, 但所有的 Vector 的方法都具有 synchronized 关键修饰。但对于复合操作,Vector 仍然需要进行同步处理。

从 Java5 开始,Java.util.concurent 包下,提供了大量支持高效并发的访问的集合类。Vector 其实已经被JDK抛弃了,只是为了向前兼容,还留着它们。Vector 本身没有解决多线程问题,反而,在引入了 synchronized 概念的混乱的同时,导致性能问题,因为 synchronized 的开销是巨大的:阻止编译器乱序。

所以,以后再有人给你说 Vector 是线程安全的时候,请加条件,否则就打他的脸!

业余草公众号

最后,欢迎关注我的个人微信公众号:业余草(yyucao)!可加QQ1群:135430763(2000人群已满),QQ2群:454796847,QQ3群:187424846。QQ群进群密码:xttblog,想加微信群的朋友,可以微信搜索:xmtxtt,备注:“xttblog”,添加助理微信拉你进群。备注错误不会同意好友申请。再次感谢您的关注!后续有精彩内容会第一时间发给您!原创文章投稿请发送至532009913@qq.com邮箱。商务合作可添加助理微信进行沟通!

本文原文出处:业余草: » Vector 真的线程安全的吗?