往map里的vector添加_面试官问我同步容器(如Vector)的所有操作一定是线程安全的吗?我懵了!...

为了方便编写出线程安全的程序,Java里面提供了一些线程安全类和并发工具,比如:同步容器、并发容器、阻塞队列等。

最常见的同步容器就是Vector和Hashtable了,那么,同步容器的所有操作都是线程安全的吗?

这个问题不知道你有没有想过,本文就来深入分析一下这个问题,一个很容易被忽略的问题。

1

同步容器

在Java中,同步容器主要包括2类:

  • 1、Vector、Stack、HashTable
  • 2、Collections类中提供的静态工厂方法创建的类
本文拿相对简单的Vecotr来举例,我们先来看下Vector中几个重要方法的源码:
public synchronized boolean add(E e) {    modCount++;    ensureCapacityHelper(elementCount + 1);    elementData[elementCount++] = e;    return true;}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;}public synchronized E get(int index) {    if (index >= elementCount)        throw new ArrayIndexOutOfBoundsException(index);    return elementData(index);}
可以看到,Vector这样的同步容器的所有公有方法全都是synchronized的,也就是说,我们可以在多线程场景中放心的使用单独这些方法,因为这些方法本身的确是线程安全的。但是,请注意上面这句话中,有一个比较关键的词:单独因为,虽然同步容器的所有方法都加了锁,但是对这些容器的复合操作无法保证其线程安全性。需要客户端通过主动加锁来保证。简单举一个例子,我们定义如下删除Vector中最后一个元素方法:
public Object deleteLast(Vector v){    int lastIndex  = v.size()-1;    v.remove(lastIndex);}
上面这个方法是一个复合方法,包括size()和remove(),乍一看上去好像并没有什么问题,无论是size()方法还是remove()方法都是线程安全的,那么整个deleteLast方法应该也是线程安全的。但是时,如果多线程调用该方法的过程中,remove方法有可能抛出ArrayIndexOutOfBoundsException。
Exception in thread "Thread-1" java.lang.ArrayIndexOutOfBoundsException: Array index out of range: 879    at java.util.Vector.remove(Vector.java:834)    at com.hollis.Test.deleteLast(EncodeTest.java:40)    at com.hollis.Test$2.run(EncodeTest.java:28)    at java.lang.Thread.run(Thread.java:748)
我们上面贴了remove的源码,我们可以分析得出:当index >= elementCount时,会抛出ArrayIndexOutOfBoundsException ,也就是说,当当前索引值不再有效的时候,将会抛出这个异常。因为removeLast方法,有可能被多个线程同时执行,当线程2通过index()获得索引值为10,在尝试通过remove()删除该索引位置的元素之前,线程1把该索引位置的值删除掉了,这时线程一在执行时便会抛出异常。
224757534875328736cf608d6c98882d.png
为了避免出现类似问题,可以尝试加锁:
public void deleteLast() {    synchronized (v) {        int index = v.size() - 1;        v.remove(index);    }}
如上,我们在deleteLast中,对v进行加锁,即可保证同一时刻,不会有其他线程删除掉v中的元素。另外,如果以下代码会被多线程执行时,也要特别注意:
for (int i = 0; i     v.remove(i);}
由于,不同线程在同一时间操作同一个Vector,其中包括删除操作,那么就同样有可能发生线程安全问题。所以,在使用同步容器的时候,如果涉及到多个线程同时执行删除操作,就要考虑下是否需要加锁。

2

同步容器的问题

前面说过了,同步容器直接保证单个操作的线程安全性,但是无法保证复合操作的线程安全,遇到这种情况时,必须要通过主动加锁的方式来实现。而且,除此之外,同步容易由于对其所有方法都加了锁,这就导致多个线程访问同一个容器的时候,只能进行顺序访问,即使是不同的操作,也要排队,如get和add要排队执行。这就大大的降低了容器的并发能力。

3

并发容器

针对前文提到的同步容器存在的并发度低问题,从Java5开始,java.util.concurent包下,提供了大量支持高效并发的访问的集合类,我们称之为并发容器。
7eb2e4d9339a3ce524da720e07ff8e5e.png
针对前文提到的同步容器的复合操作的问题,一般在Map中发生的比较多,所以在ConcurrentHashMap中增加了对常用复合操作的支持,比如putIfAbsent()、replace(),这2个操作都是原子操作,可以保证线程安全。另外,并发包中的CopyOnWriteArrayList和CopyOnWriteArraySet是Copy-On-Write的两种实现。Copy-On-Write容器即写时复制的容器。通俗的理解是当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器。CopyOnWriteArrayList中add/remove等写方法是需要加锁的,而读方法是没有加锁的。这样做的好处是我们可以对CopyOnWrite容器进行并发的读,当然,这里读到的数据可能不是最新的。因为写时复制的思想是通过延时更新的策略来实现数据的最终一致性的,并非强一致性。但是,作为代替Vector的CopyOnWriteArrayList并没有解决同步容器的复合操作的线程安全性问题。

4

总结

本文介绍了同步容器和并发容器。同步容器是通过加锁实现线程安全的,并且只能保证单独的操作是线程安全的,无法保证复合操作的线程安全性。并且同步容器的读和写操作之间会互相阻塞。并发容器是Java 5中提供的,主要用来代替同步容器。有更好的并发能力。而且其中的ConcurrentHashMap定义了线程安全的复合操作。在多线程场景中,如果使用并发容器,一定要注意复合操作的线程安全问题。必要时候要主动加锁。在并发场景中,建议直接使用java.util.concurent包中提供的容器类,如果需要复合操作时,建议使用有些容器自身提供的复合方法。

有道无术,术可成;有术无道,止于术

欢迎大家关注Java之道公众号

f8d7d94d429c38322e39f4176ec747d5.png

好文章,我在看❤️

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/562062.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

HTML+CSS+JS实现 ❤️3D方块弹跳动画特效❤️

效果演示: 代码目录: 主要代码实现: 部分CSS样式: *, *::before, *::after {padding: 0;margin: 0 auto;box-sizing: border-box;transform-style: preserve-3d; }body {background-color: black;min-height: 100vh;display: f…

HTML+CSS+JS实现 ❤️520爱心背景表白网页动画特效❤️

效果演示&#xff1a; 代码目录&#xff1a; 主要代码实现&#xff1a; 部分HTML代码 : <!DOCTYPE html> <html><head><meta http-equiv"Content-Type" content"text/html; charsetUTF-8"><title>好朋友-相册</title&…

HTML+CSS+JS实现 ❤️three 3D铅笔绘图工具特效❤️

效果演示&#xff1a; 代码目录&#xff1a; 主要代码实现&#xff1a; 部分CSS样式&#xff1a; #mfPreviewBar {display: none !important; }html, body {position: fixed;overflow: hidden;touch-action: none; }body {background-color: #f7f4f0;cursor: url("http…

mysql linux改密码忘记了怎么办_linux上mysql改密码忘了怎么办?

解决方法&#xff1a;1、在my.cnf中&#xff0c;增加“[mysqld]”和“skip-grant-tables”2行代码&#xff0c;用于登录时跳过密码验证&#xff1b;2、启动mysql服务&#xff0c;并登录mysql&#xff1b;3、连接mysql数据库&#xff0c;使用UPDATE命令修改用户密码即可。linux上…

HTML+CSS+JS实现 ❤️透明的水滴文字背景特效❤️

效果演示&#xff1a; 代码目录&#xff1a; 主要代码实现&#xff1a; 部分CSS样式&#xff1a; import url(https://fonts.googleapis.com/css2?familyMontserrat:wght100&displayswap); import url(https://fonts.googleapis.com/css2?familyPoppins:wght900&d…

HTML+CSS+JS实现 ❤️3D旋转魔方图片相册特效❤️

效果演示&#xff1a; 代码目录&#xff1a; 主要代码实现&#xff1a; 部分CSS样式&#xff1a; background: url(../img/s3.jpg);background-size: 100% 100%;opacity: 0.8;transform: rotateY(90deg) translateZ(200px); }.box .box1 .left {background: url(../img/s4.jp…

HTML+CSS+JS实现 ❤️响应式的幸运大转盘❤️

效果演示&#xff1a; 代码目录&#xff1a; 主要代码实现&#xff1a; 部分CSS样式&#xff1a; .winnerBox {max-width: 40rem;padding: 30px;margin: 30px auto;/*height: calc(100vh - 98px);*//*background-color: #fd6504 #9470fd;*/background: linear-gradient(to b…

maya python 开根号_maya python

胡泳滨MayaPython简易教程&#xff0c;如需转载&#xff0c;请标明出处地址&#xff1a;http://huyongbin.blogbus.com/c3363976/谢谢配合&#xff01;MayaPython第一篇 - 介绍大家好&#xff0c;这是一个简易的MAYA PYTHON学习教程。简易教程的目的是可以让更多的人快速掌握这…

HTML+CSS+JS实现 ❤️发光的线条爱心形状动画特效❤️

效果演示&#xff1a; 代码目录&#xff1a; 主要代码实现&#xff1a; 部分CSS样式&#xff1a; body {background-color: #000;margin: 0;overflow: hidden;background-repeat: no-repeat; } HTML代码 : <!DOCTYPE html> <html lang"en"><head…

mysql 钩子_面试官: 什么是 Hook (钩子) 线程以及应用场景?

一、Hook 线程介绍通常情况下&#xff0c;我们可以向应用程序注入一个或多个 Hook (钩子) 线程&#xff0c;这样&#xff0c;在程序即将退出的时候&#xff0c;也就是 JVM 程序即将退出的时候&#xff0c;Hook 线程就会被启动执行。先看一段示例代码&#xff1a;①&#xff1a;…

HTML+CSS+JS实现 ❤️H5 3D传送带视差照片特效❤️

效果演示&#xff1a; 代码目录&#xff1a; 主要代码实现&#xff1a; 部分CSS样式&#xff1a; html, body, .stage, .ring, .img {width: 100%;height: 100%;transform-style: preserve-3d;user-select: none; }html, body, .stage {overflow: hidden;background: #000; …

HTML+CSS+JS实现 ❤️h5酷炫的天体木星动画特效❤️

效果演示&#xff1a; 代码目录&#xff1a; 主要代码实现&#xff1a; 部分CSS样式&#xff1a; body {margin: 0;overflow: hidden;position: relative;width: 100vw;height: 100vh;background-image: url("../img/bg.jpg");background-size: cover; }canvas {d…

HTML+CSS+JS实现 ❤️彩色3D线条动画特效❤️

效果演示&#xff1a; 代码目录&#xff1a; 主要代码实现&#xff1a; HTML代码 : <!doctype html> <html><head><meta charset"utf-8"><title>彩带特效</title></head><body><script id"fs" type…

HTML+CSS+JS实现 ❤️卡通人物吃水果游戏❤️

效果演示&#xff1a; 代码目录&#xff1a; 主要代码实现&#xff1a; HTML代码 : <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial…

py哪个函数可以返回输入的变量类型_[Python基础]二、pycharm,python变量

2.1 Python简介Life is short,you need Python (人生苦短&#xff0c;我用Python)解释器&#xff1a;将其他语言翻译成机器语言的工具&#xff0c;称为编译器编译器的翻译方法有两种&#xff1a;编译解释Python的设计哲学优雅明确简单Python开发者哲学&#xff1a;用一种方法&a…

HTML+CSS+JS实现 贪吃蛇游戏源码

效果演示&#xff1a; 文末获取源码 代码目录&#xff1a; 主要代码实现&#xff1a; 部分代码 : <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><meta http-equiv&qu…

HTML+CSS+JS实现 ❤️个人相册封面卡片❤️

效果演示&#xff1a; 代码目录&#xff1a; 主要代码实现&#xff1a; CSS样式&#xff1a; charset "utf-8"; import url("https://s2.pstatp.com/cdn/expire-1-M/font-awesome/4.7.0/css/font-awesome.min.css"); body {background-color: #1F1F1F;o…

HTML+CSS+JS实现 ❤️swiper倾斜图片特效❤️

效果演示&#xff1a; 代码目录&#xff1a; 主要代码实现&#xff1a; CSS样式&#xff1a; .img_swiper {width: 800px;margin: 0 auto;position: relative; }.img {width: 100%;height: 100%; }.img_swiper .swiper-button-prev {width: 40px;height: 40px;background-im…

如何将c语言程序封装供python调用_C++调用python

C调用python在C/C中嵌入Python&#xff0c;可以使用Python提供的强大功能&#xff0c;通过嵌入Python可以替代动态链接库形式的接口&#xff0c;这样可以方便地根据需要修改脚本代码&#xff0c;而不用重新编译链接二进制的动态链接库。至少你可以把它当成文本形式的动态链接库…

HTML+CSS+JS实现 ❤️touchSlider图片滚动图片轮播❤️

效果演示&#xff1a; 代码目录&#xff1a; 主要代码实现&#xff1a; CSS样式&#xff1a; charset "utf-8"; * {margin: 0;padding: 0;list-style: none;border: 0; }body {width: 100%;margin: 0 auto;overflow: hidden }/* main_image */.main_visual {heigh…