「这是我参与11月更文挑战的第22天,活动详情查看:2021最后一次更文挑战」
本文被《从小工到专家的 Java 进阶之旅》收录。
你好,我是看山。
本文还是折腾 Java 中的队列,上次比较了 Vector、ArrayList、CopyOnWriteArrayList、SynchronizedList,当时感觉挺明白,后来想想又有些不理解的地方,所以今天在重新翻出来研究一下,我承认我钻了牛角尖了。
Vector
虽然种种问题,但是都属于设计上的问题,为什么不在后续版本中进行优化呢?HashMap
就优化了好几次。而SynchronizedList
这个内部类(也就是通过Collections.synchronizedList(new ArrayList())
创建的),也是采用了和Vector
类似的同步方式(区别是一个在方法体、一个在方法块上,差别不大),为什么大家还是舍弃Vector
呢?
其实,在 JDK 中,Vector
一直没有被标记为Deprecated
,也就是说,虽然外界传说Vector
有各种问题,但是从 JDK 官方,从没有认为这个亲儿子没用。
所以,大家不用Vector
的原因就剩下两种:
- 其他队列比
Vector
更加适合,优中选优 - 大家都说
Vector
不好用,那我也不用了【个人感觉这种概率更大】
因为
Vector
主要是数组结构,所以下面大部分的对比都是比较的是针对ArrayList
的同步封装。
有了Vector
为什么还要有SynchronizedList
这个问题的答案是从 StackOverflow 中找到的。
在 JDK 1.2 之前,Collections
是独立类库,不是 JDK/JRE 中的一部分。当时synchronized
性能特别差,很多场景不需要使用同步方式,所以,独立类库的开发者删除了同步操作,这个应该就是ArrayList
的前身。但是,少部分场景还是需要使用同步,于是就有了SynchronizedList
,一个可以包装所有List
子类的包装类,这个类在几乎所有方法上都加上了synchronized
同步,这个设计与Vector
相似。
古人说“文人相轻”,其实在编码界也是有鄙视链的。在这里就是:虽然我的设计和你的设计类似,但是我的设计就是比你的好。不过,Collections
确实设计更优。
一个SynchronizedList
实现所有List
的同步
SynchronizedList
定位是包装类,可以包装所有List
的子类。也就是说,无论是ArrayList
还是LinkedList
都能过实现同步,完全不会修改底层数据结构,既实现的同步,又保留了底层接口的优点。比如LinkedList
的插入、删除效率,ArrayList
的顺序读取。而且,一个包装类就解决所有List
子类的同步需求,完全不需要重复实现一遍。
相对而言,Vector
就比较霸道了,任何想要同步的队列,都需要转换为Vector
的数组结构。大家都知道,数组存储需要连续空间,顺序读取效率表现优秀,但是插入和删除效率就比较差了。
将迭代器的同步权利交给用户
同步方法中SynchronizedList
和Vector
很类似,不过迭代器方法有了不同想法。
看源码就知道,SynchronizedList
中的iterator
和listIterator
方法都没有实现同步,所以在获取迭代器的时候不会阻塞。
1 | java复制代码public Iterator<E> iterator() { |
如果需要迭代的话,直接用synchronized
包一下队列对象就可以了,代码如下:
1 | java复制代码final List<String> list = Collections.synchronizedList(new ArrayList()); |
我们再看下Vector
迭代器实现:
1 | java复制代码/** |
Vector
的迭代器用synchronized (Vector.this)
加锁,其实也是对当前类实例加锁,和我们自己实现的加锁方式一致。当然,从这点上来说,Vector
能够保证在开发人员无意识的情况下,避免为同步造成的错误,这也是Vector
的一个优点。
Vector
不完全一无是处
虽然Vector
在其他地方败给了Collections
,但是在扩容这方面,还有一个可取之处。先看看Vector
的扩容方法:
1 | java复制代码private void grow(int minCapacity) { |
在计算新数组长度的时候,会检查capacityIncrement
是否大于 0,如果是,就扩容capacityIncrement
的大小。就是说,在Vector
中可以指定扩容大小,如果没有指定,默认扩容到原来的 2 倍;而ArrayList
只能扩容到 1.5 倍,没有办法自定义扩容大小。
仔细想想,这点并没有什么用处。
文末总结
Vector
内部结构是数组,与Collections.synchronizedList(new ArrayList())
类似。Vector
可以指定扩容大小,默认是扩容到原数组长度的 2 倍;ArrayList
不能指定扩容大小,直接扩容到原数组大小的 1.5 倍。SynchronizedList
是一个包装类,可以将List
子类都包装为同步队列,从非线程安全队列转为线程安全队列,没有性能延迟,直接包装即可;Vector
是一个基于数组的同步队列,其他队列想要转换为Vector
,需要有数据拷贝。SynchronizedList
的迭代器没有做同步,需要用户自己实现;Vector
的迭代器做好了同步,开发人员不需要关心同步。Vector
至今未标记Deprecated
,而且随着 JDK 发布,也在更新实现。虽然 JDK 承诺兼容,但是一直没有标记过期,其用意不得而知。
推荐阅读
- JDK中居然也有反模式接口常量
- java import 导入包时,我们需要注意什么呢?
- Java 并发基础(一):synchronized 锁同步
- Java 并发基础(二):主线程等待子线程结束
- Java 并发基础(三):再谈 CountDownLatch
- Java 并发基础(四):再谈 CyclicBarrier
- Java 并发基础(五):面试实战之多线程顺序打印
- 如果非要在多线程中使用ArrayList会发生什么?
- 如果非要在多线程中使用 ArrayList 会发生什么?(第二篇)
- 重新认识 Java 中的队列
- Java 中 Vector 和 SynchronizedList 的区别
- 一文掌握 Java8 Stream 中 Collectors 的 24 个操作
- 一文掌握 Java8 的 Optional 的 6 种操作
你好,我是看山。游于码界,戏享人生。如果文章对您有帮助,请点赞、收藏、关注。欢迎关注公众号「看山的小屋」,发现不一样的世界。
本文转载自: 掘金