Java中Vector如何实现List的并发访问机制?

2026-05-27 03:421阅读0评论SEO问题
  • 内容介绍
  • 文章标签
  • 相关推荐

在Java浩如烟海的集合类库中,Vector算得上是一位资历颇深的“老将”。如果你翻开JDK 1.0的源码,会发现它早已屹立在那里。只是因为现代Java开发的演进, 这位老将似乎逐渐淡出了主流视野,取而代之的是ArrayList和CopyOnWriteArrayList等后起之秀。 我深信... 但不可否认的是Vector在处理多线程并发访问时依然有其独特的教科书般的意义。今天我们就抛开那些枯燥的定义, 像拆解一台老式收音机一样,Vector究竟是如何实现List的并发访问的,以及为什么它在如今的高并发场景下显得有些“力不从心”。

Vector的实现原理初探

我们要理解Vector的并发机制,得看清它的“真面目”。从类的定义上来看, Vector继承自AbstractList一边实现了ListRandomAccessCloneable以及java.io.Serializable接口。这看起来和ArrayList简直像是一个模子里刻出来的,对吧?确实它们在底层数据结构上有着惊人的相似度。

Java中Vector如何实现List的并发访问机制?

如果你点开Vector的源码, 最引人注目的莫过于那几个核心成员变量:

protected Object elementData;
protected int elementCount;
protected int capacityIncrement;

将心比心... 这里的elementData就是一个对象数组,用来存放真正的数据;elementCount记录了当前数组里实际有多少个元素。这和ArrayList的套路如出一辙。但是请注意那个capacityIncrement 这是Vector特有的一个“私房钱”,它决定了当数组不够用时Vector该扩容多少。不过这并不是我们今天讨论的重点,我们今天的焦点在于——并发。

"粗粒度锁":简单的线程平安方案

累并充实着。 说到并发,很多新手程序员可能会觉得云里雾里又是CAS又是AQS的。但在Vector那个年代, 解决方案简单而直接:synchronized. 这就是`Vector`实现线程平安的全部秘密——没有花哨的技巧,就是一把大锁。

`Vector` 的几乎所有涉及数据修改或查询的方法都无一例外地加上了`synchronized`修饰符。我们可以随便看看几个核心方法,比如 `add`、`remove` 或者 `get`。比如 `add` 方法: ``publ 弯道超车。 ic synchronized boolean add { modCount++; ensureCapacityHelper; elementData = e; return true;}` 看到了吗?

Java中Vector如何实现List的并发访问机制?

所以_Vector_ 虽然De 被时代洪流所淘汰,但Ta De 设计思想和历史Di 位并不会被遗忘。对于开发者而言, 了解_Vector_ 背hou De 并发控制机制,不仅能帮Ning 我们更好地维护老旧代码,更能让我们深刻理解现代Java并Geng 发编程De 精髓所在,坦白讲...。

不是我唱反调... 你不需要懂什么是 volatile, 也不需要懂什么是 CAS,只要用_Vector_ ,数据jiu 不会乱。 但技术shi 不断进步de 。_vector 用一种及其保守De 方Shi 实现Liao 并发平安_Sacrificed_ ,牺牲Liao 性Neng 换取Liao 平安。而Zai 今天我们有Geng 多、 Geng 优Ya De Gong 具Lai 平衡性Neng 与平安,比如 _concurrenthashmap_copyonwritearraylist_ Deng 等等。

毕竟谁会愿意Yi 在高铁时代还去坐蒸汽火车呢?除非,你真的只是为了欣赏沿途de 风景。) Sui 然失败但值得铭记 回过头来我们再看 Vector。它是一个失败De 产品ma ? 栓Q! 也不尽然。或者说 在一些对性能要求不高的遗留系统中,或者教学实践de 场合下_Vector 提供了一种开箱即用的线An 全方案_Shen ,极大地降低Liao 开发者的心智负担。

它告诉我们, 虽然 synchronized 虽然好用,但不可滥用;在追求现今仍thread-safeNeng 线程平安的道路 , 上jia 上jia ,lock 的粒度、读写分离de 策略都是我们需要深思熟虑的问题。 算是吧... ) 下次当你再看到 Vector 的时候, 不妨对这位老将多一份敬意,但在写新代码时还是请把它留在记忆JButton博物馆吧。

这是可以说的吗? 虽然 Vector 本身的方法是同步 , 但 Ru 果你在业务代码中混合使用了 Vector 和其他同步块,Zui 后 lock 的层级一旦复杂起来维护起来简直就是噩梦。) 所以理解 Vector 如何实现并发访问, 不仅仅是为了去维护那些老旧的代码,更是为了理解Java并发编程Sui 着时间的演变。

这种“粗粒度锁”带来的性能瓶颈是无法接受的。 还有啊 Vector 还有一个让人诟病的地方:它不仅锁住了当前线程, 还可能主要原因是代码逻辑的不当,导致死锁的风险。虽然 Vector 自身的方法是同步的, 但如果你在业务代码中混合使用了 Vector 和其他同步块once locks),Neng 会导致死锁的风险。

放心去做... 这就像是你虽然买了票进了电影院,但如果你不按座位坐,管理员还是会赶你走一样。 "老将"为何被遗弃? 看到这里你可能会问: Vector 这么平安,这么简单,为什么现在大家都不爱用了呢?甚至很多大厂的规范里直接禁止使用 Vector。 原因就在于那个 synchronized 。虽然它保证了线程平安,但代价是巨大的——性能损耗。

不过这里有个坑。虽然 Vector 的方法都加了锁, 可以防止多线程一边修改导致的数据错乱,但它并不能完全杜绝 ConcurrentModificationException。为什么?主要原因是虽然 Vector 保证了操作的原子性, 不错。 但如果你在一个线程中用迭代器遍历,而在另一个线程中直接调用 Vector 的 remove 方法,虽然数据不会坏,但迭代器内部的 modCount 校验机制依然可能会抛出异常。

这里有一个 `synchronized ` 代码块。这意味着, 即使你拿到的是迭代器,当你调用 `next` 去取下一个元素时它依然会去尝试获取 `Vector` 实例的对象锁。 那必须的! 这种设计确保了在遍历的过程中, 如果有其他线程试图修改 `Vector的结构,它们会被挡在门外从而保证了当前遍历操作的数据一致性。

说真的... `Vector` 的迭代器实现 除了增删改查,我们在实际开发中经常需要遍历List。这时候,`Vector` 又是如何保证平安的呢?这就不得不提它的 `iterator` 方法了。 `public synchronized Iterator `iterator { return new Itr; } 我们再深入看看 `Itr` 内部的 `next` 方法: `public E next { synchronized { checkForComodification; int i = cursor; if throw new NoSuchElementException; cursor = i + 1; return elementData; } } 注意到了吗?

`CopyOnWriteArrayList` 的思路就完全不同了。它写操作的时候,会先复制一份整个数组,在副本上修改,修改完之后再替换掉原来的数组引用。在这个过程中,读操作是不需要加锁的,可以直接读旧数组。这就实现了读写分离,极大地提高了读多写少场景下的性能。比一比的话 `Vector` 不管你是读还是写,统统都要加锁,效率自然就低下了,我惊呆了。。

我CPU干烧了。 "粗粒度锁"带来的性能瓶颈 在现代互联网应用中,并发量动辄成千上万。如果所有线程都要排队过这一座独木桥,系统的吞吐量怎么可能上得去?这就好比早高峰的地铁站,如果只开一个安检口,那队伍能排到二环去。这就是所谓的“锁竞争”问题。 为了解决这个问题, Java后续引入了 `java.util.concurrent` 包,里面提供了更高级的并发容器,比如 `CopyOnWriteArrayList`。

为什么叫粗粒度?主要原因是它锁住了整个方法体,而不是方法中的某一小段代码。这就好比你要上厕所,不管你是进去洗手还是上厕所,只要门锁了别人就进不去。这种做法虽然简单粗暴, 翻车了。 容易理解,但也带来了显而易见的性能问题。在并发量不高的情况下这当然没问题;可一旦有几十上百个线程一边争抢这把锁, 那场面简直就是“堵车”现场,性能会呈指数级下降。

那个 `synchronized` 关键字就像一个尽职尽责的保安,站在方法门口。这意味着什么呢?这意味着在任何时刻,只有一个线程能够施行这个 `add` 方法。当线程A抢到了锁, 它开始往数组里塞数据,此时线程B、C、D只能在门外排队等候,直到线程A施行完毕,释放了锁,下一个线程才能进来。 同样的逻辑也适用于 `remove` 方法: `public synchronized E remove { modCount++; if throw new ArrayIndexOutOfBoundsException; E oldValue = elementData; int numMoved = elementCount - index - 1; if System.arraycopy; elementData = null; // Let gc do its work return oldValue;} 这种机制被称为“粗粒度锁”。

标签:Java

在Java浩如烟海的集合类库中,Vector算得上是一位资历颇深的“老将”。如果你翻开JDK 1.0的源码,会发现它早已屹立在那里。只是因为现代Java开发的演进, 这位老将似乎逐渐淡出了主流视野,取而代之的是ArrayList和CopyOnWriteArrayList等后起之秀。 我深信... 但不可否认的是Vector在处理多线程并发访问时依然有其独特的教科书般的意义。今天我们就抛开那些枯燥的定义, 像拆解一台老式收音机一样,Vector究竟是如何实现List的并发访问的,以及为什么它在如今的高并发场景下显得有些“力不从心”。

Vector的实现原理初探

我们要理解Vector的并发机制,得看清它的“真面目”。从类的定义上来看, Vector继承自AbstractList一边实现了ListRandomAccessCloneable以及java.io.Serializable接口。这看起来和ArrayList简直像是一个模子里刻出来的,对吧?确实它们在底层数据结构上有着惊人的相似度。

Java中Vector如何实现List的并发访问机制?

如果你点开Vector的源码, 最引人注目的莫过于那几个核心成员变量:

protected Object elementData;
protected int elementCount;
protected int capacityIncrement;

将心比心... 这里的elementData就是一个对象数组,用来存放真正的数据;elementCount记录了当前数组里实际有多少个元素。这和ArrayList的套路如出一辙。但是请注意那个capacityIncrement 这是Vector特有的一个“私房钱”,它决定了当数组不够用时Vector该扩容多少。不过这并不是我们今天讨论的重点,我们今天的焦点在于——并发。

"粗粒度锁":简单的线程平安方案

累并充实着。 说到并发,很多新手程序员可能会觉得云里雾里又是CAS又是AQS的。但在Vector那个年代, 解决方案简单而直接:synchronized. 这就是`Vector`实现线程平安的全部秘密——没有花哨的技巧,就是一把大锁。

`Vector` 的几乎所有涉及数据修改或查询的方法都无一例外地加上了`synchronized`修饰符。我们可以随便看看几个核心方法,比如 `add`、`remove` 或者 `get`。比如 `add` 方法: ``publ 弯道超车。 ic synchronized boolean add { modCount++; ensureCapacityHelper; elementData = e; return true;}` 看到了吗?

Java中Vector如何实现List的并发访问机制?

所以_Vector_ 虽然De 被时代洪流所淘汰,但Ta De 设计思想和历史Di 位并不会被遗忘。对于开发者而言, 了解_Vector_ 背hou De 并发控制机制,不仅能帮Ning 我们更好地维护老旧代码,更能让我们深刻理解现代Java并Geng 发编程De 精髓所在,坦白讲...。

不是我唱反调... 你不需要懂什么是 volatile, 也不需要懂什么是 CAS,只要用_Vector_ ,数据jiu 不会乱。 但技术shi 不断进步de 。_vector 用一种及其保守De 方Shi 实现Liao 并发平安_Sacrificed_ ,牺牲Liao 性Neng 换取Liao 平安。而Zai 今天我们有Geng 多、 Geng 优Ya De Gong 具Lai 平衡性Neng 与平安,比如 _concurrenthashmap_copyonwritearraylist_ Deng 等等。

毕竟谁会愿意Yi 在高铁时代还去坐蒸汽火车呢?除非,你真的只是为了欣赏沿途de 风景。) Sui 然失败但值得铭记 回过头来我们再看 Vector。它是一个失败De 产品ma ? 栓Q! 也不尽然。或者说 在一些对性能要求不高的遗留系统中,或者教学实践de 场合下_Vector 提供了一种开箱即用的线An 全方案_Shen ,极大地降低Liao 开发者的心智负担。

它告诉我们, 虽然 synchronized 虽然好用,但不可滥用;在追求现今仍thread-safeNeng 线程平安的道路 , 上jia 上jia ,lock 的粒度、读写分离de 策略都是我们需要深思熟虑的问题。 算是吧... ) 下次当你再看到 Vector 的时候, 不妨对这位老将多一份敬意,但在写新代码时还是请把它留在记忆JButton博物馆吧。

这是可以说的吗? 虽然 Vector 本身的方法是同步 , 但 Ru 果你在业务代码中混合使用了 Vector 和其他同步块,Zui 后 lock 的层级一旦复杂起来维护起来简直就是噩梦。) 所以理解 Vector 如何实现并发访问, 不仅仅是为了去维护那些老旧的代码,更是为了理解Java并发编程Sui 着时间的演变。

这种“粗粒度锁”带来的性能瓶颈是无法接受的。 还有啊 Vector 还有一个让人诟病的地方:它不仅锁住了当前线程, 还可能主要原因是代码逻辑的不当,导致死锁的风险。虽然 Vector 自身的方法是同步的, 但如果你在业务代码中混合使用了 Vector 和其他同步块once locks),Neng 会导致死锁的风险。

放心去做... 这就像是你虽然买了票进了电影院,但如果你不按座位坐,管理员还是会赶你走一样。 "老将"为何被遗弃? 看到这里你可能会问: Vector 这么平安,这么简单,为什么现在大家都不爱用了呢?甚至很多大厂的规范里直接禁止使用 Vector。 原因就在于那个 synchronized 。虽然它保证了线程平安,但代价是巨大的——性能损耗。

不过这里有个坑。虽然 Vector 的方法都加了锁, 可以防止多线程一边修改导致的数据错乱,但它并不能完全杜绝 ConcurrentModificationException。为什么?主要原因是虽然 Vector 保证了操作的原子性, 不错。 但如果你在一个线程中用迭代器遍历,而在另一个线程中直接调用 Vector 的 remove 方法,虽然数据不会坏,但迭代器内部的 modCount 校验机制依然可能会抛出异常。

这里有一个 `synchronized ` 代码块。这意味着, 即使你拿到的是迭代器,当你调用 `next` 去取下一个元素时它依然会去尝试获取 `Vector` 实例的对象锁。 那必须的! 这种设计确保了在遍历的过程中, 如果有其他线程试图修改 `Vector的结构,它们会被挡在门外从而保证了当前遍历操作的数据一致性。

说真的... `Vector` 的迭代器实现 除了增删改查,我们在实际开发中经常需要遍历List。这时候,`Vector` 又是如何保证平安的呢?这就不得不提它的 `iterator` 方法了。 `public synchronized Iterator `iterator { return new Itr; } 我们再深入看看 `Itr` 内部的 `next` 方法: `public E next { synchronized { checkForComodification; int i = cursor; if throw new NoSuchElementException; cursor = i + 1; return elementData; } } 注意到了吗?

`CopyOnWriteArrayList` 的思路就完全不同了。它写操作的时候,会先复制一份整个数组,在副本上修改,修改完之后再替换掉原来的数组引用。在这个过程中,读操作是不需要加锁的,可以直接读旧数组。这就实现了读写分离,极大地提高了读多写少场景下的性能。比一比的话 `Vector` 不管你是读还是写,统统都要加锁,效率自然就低下了,我惊呆了。。

我CPU干烧了。 "粗粒度锁"带来的性能瓶颈 在现代互联网应用中,并发量动辄成千上万。如果所有线程都要排队过这一座独木桥,系统的吞吐量怎么可能上得去?这就好比早高峰的地铁站,如果只开一个安检口,那队伍能排到二环去。这就是所谓的“锁竞争”问题。 为了解决这个问题, Java后续引入了 `java.util.concurrent` 包,里面提供了更高级的并发容器,比如 `CopyOnWriteArrayList`。

为什么叫粗粒度?主要原因是它锁住了整个方法体,而不是方法中的某一小段代码。这就好比你要上厕所,不管你是进去洗手还是上厕所,只要门锁了别人就进不去。这种做法虽然简单粗暴, 翻车了。 容易理解,但也带来了显而易见的性能问题。在并发量不高的情况下这当然没问题;可一旦有几十上百个线程一边争抢这把锁, 那场面简直就是“堵车”现场,性能会呈指数级下降。

那个 `synchronized` 关键字就像一个尽职尽责的保安,站在方法门口。这意味着什么呢?这意味着在任何时刻,只有一个线程能够施行这个 `add` 方法。当线程A抢到了锁, 它开始往数组里塞数据,此时线程B、C、D只能在门外排队等候,直到线程A施行完毕,释放了锁,下一个线程才能进来。 同样的逻辑也适用于 `remove` 方法: `public synchronized E remove { modCount++; if throw new ArrayIndexOutOfBoundsException; E oldValue = elementData; int numMoved = elementCount - index - 1; if System.arraycopy; elementData = null; // Let gc do its work return oldValue;} 这种机制被称为“粗粒度锁”。

标签:Java