往map里的vector添加_面试官:同步容器(如Vector)的所有操作一定是线程安全的吗?...
专注于Java领域优质技术,欢迎关注
作者:HollisChuang
![](/assets/blank.gif)
为了方便编写出线程安全的程序,Java里面提供了一些线程安全类和并发工具,比如:同步容器、并发容器、阻塞队列等。
最常见的同步容器就是Vector和Hashtable了,那么,同步容器的所有操作都是线程安全的吗?
这个问题不知道你有没有想过,本文就来深入分析一下这个问题,一个很容易被忽略的问题。
Java中的同步容器
在Java中,同步容器主要包括2类:
- 1、Vector、Stack、HashTable
- 2、Collections类中提供的静态工厂方法创建的类
本文拿相对简单的Vecotr来举例,我们先来看下Vector中几个重要方法的源码:
![](/assets/blank.gif)
![](/assets/blank.gif)
可以看到,Vector这样的同步容器的所有公有方法全都是synchronized的,也就是说,我们可以在多线程场景中放心的使用单独这些方法,因为这些方法本身的确是线程安全的。
但是,请注意上面这句话中,有一个比较关键的词:单独
因为,虽然同步容器的所有方法都加了锁,但是对这些容器的复合操作无法保证其线程安全性。需要客户端通过主动加锁来保证。
简单举一个例子,我们定义如下删除Vector中最后一个元素方法:
![](/assets/blank.gif)
上面这个方法是一个复合方法,包括size()和remove(),乍一看上去好像并没有什么问题,无论是size()方法还是remove()方法都是线程安全的,那么整个deleteLast方法应该也是线程安全的。
但是时,如果多线程调用该方法的过程中,remove方法有可能抛出ArrayIndexOutOfBoundsException。
![](/assets/blank.gif)
我们上面贴了remove的源码,我们可以分析得出:当index >= elementCount时,会抛出ArrayIndexOutOfBoundsException ,也就是说,当当前索引值不再有效的时候,将会抛出这个异常。
因为removeLast方法,有可能被多个线程同时执行,当线程2通过index()获得索引值为10,在尝试通过remove()删除该索引位置的元素之前,线程1把该索引位置的值删除掉了,这时线程一在执行时便会抛出异常。
![](/assets/blank.gif)
为了避免出现类似问题,可以尝试加锁:
![](/assets/blank.gif)
如上,我们在deleteLast中,对v进行加锁,即可保证同一时刻,不会有其他线程删除掉v中的元素。
另外,如果以下代码会被多线程执行时,也要特别注意:
![](/assets/blank.gif)
由于,不同线程在同一时间操作同一个Vector,其中包括删除操作,那么就同样有可能发生线程安全问题。所以,在使用同步容器的时候,如果涉及到多个线程同时执行删除操作,就要考虑下是否需要加锁。
同步容器的问题
前面说过了,同步容器直接保证耽搁操作的线程安全性,但是无法保证复合操作的线程安全,遇到这种情况时,必须要通过主动加锁的方式来实现。
而且,除此之外,同步容易由于对其所有方法都加了锁,这就导致多个线程访问同一个容器的时候,只能进行顺序访问,即使是不同的操作,也要排队,如get和add要排队执行。这就大大的降低了容器的并发能力。
并发容器
针对前文提到的同步容器存在的并发度低问题,从Java5开始,java.util.concurent包下,提供了大量支持高效并发的访问的集合类,我们称之为并发容器。
![](/assets/blank.gif)
针对前文提到的同步容器的复合操作的问题,一般在Map中发生的比较多,所以在ConcurrentHashMap中增加了对常用复合操作的支持,比如"若没有则添加":putIfAbsent(),替换:replace()。这2个操作都是原子操作,可以保证线程安全。
另外,并发包中的CopyOnWriteArrayList和CopyOnWriteArraySet是Copy-On-Write的两种实现。
Copy-On-Write容器即写时复制的容器。通俗的理解是当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器。
CopyOnWriteArrayList中add/remove等写方法是需要加锁的,而读方法是没有加锁的。
这样做的好处是我们可以对CopyOnWrite容器进行并发的读,当然,这里读到的数据可能不是最新的。因为写时复制的思想是通过延时更新的策略来实现数据的最终一致性的,并非强一致性。
但是,作为代替Vector的CopyOnWriteArrayList并没有解决同步容器的复合操作的线程安全性问题。
总结
本文介绍了同步容器和并发容器。
同步容器是通过加锁实现线程安全的,并且只能保证单独的操作是线程安全的,无法保证复合操作的线程安全性。并且同步容器的读和写操作之间会互相阻塞。
并发容器是Java 5中提供的,主要用来代替同步容器。有更好的并发能力。而且其中的ConcurrentHashMap定义了线程安全的复合操作。
在多线程场景中,如果使用并发容器,一定要注意复合操作的线程安全问题。必要时候要主动加锁。
在并发场景中,建议直接使用java.util.concurent包中提供的容器类,如果需要复合操作时,建议使用有些容器自身提供的复合方法。
来源:掘金 链接:https://juejin.im/post/5d633d25f265da03bc1284da
往map里的vector添加_面试官:同步容器(如Vector)的所有操作一定是线程安全的吗?...相关推荐
- 往map里的vector添加_面试官问我同步容器(如Vector)的所有操作一定是线程安全的吗?我懵了!...
为了方便编写出线程安全的程序,Java里面提供了一些线程安全类和并发工具,比如:同步容器.并发容器.阻塞队列等. 最常见的同步容器就是Vector和Hashtable了,那么,同步容器的所有操作都是线 ...
- sqlserver2008未将对象引用设置到对象的实例_面试官:ThreadLocal 的内存泄漏是弱引用导致的,你确定?...
面试官:ThreadLocal 了解吗? Python 小星:线程局部变量,多线程下能保证各个线程的变量相对独立于其他线程的变量. 面试官:那你说下它是如何保证线程隔离的? Python 小星:每个线 ...
- hashmap扩容_面试官问:HashMap在并发情况下为什么造成死循环?一脸懵
这个问题是在面试时常问的几个问题,一般在问这个问题之前会问Hashmap和HashTable的区别?面试者一般会回答:hashtable是线程安全的,hashmap是线程不安全的. 那么面试官就会紧接 ...
- 如何把class里的vector结构体memcpy出来_面试官:请说出线程安全的 ArrayList 有哪些,除了Vector...
以下环境是 JDK 1.8 ArrayList 的初始容量 面试官:你看过 ArrayList 的源码? Python 小星:看过 面试官:那你说下ArrayList 的初始容量是多少? Python ...
- arraylist 后往前遍历_面试官:请说出线程安全的 ArrayList 有哪些,除了Vector
以下环境是 JDK 1.8 ArrayList 的初始容量 面试官:你看过 ArrayList 的源码? Python 小星:看过 面试官:那你说下ArrayList 的初始容量是多少? Python ...
- 面试java你最擅长什么_面试官最喜欢问的10道Java面试题
1.Java的HashMap是如何工作的? HashMap是一个针对数据结构的键值,每个键都会有相应的值,关键是识别这样的值. HashMap 基于 hashing 原理,我们通过 put ()和 g ...
- qps是什么意思_面试官:说说你之前负责的系统,QPS 能达到多少?
被面试官经常问到之前开发的系统接口 QPS 能达到多少,经常给不出一个数值,支支吾吾,导致整体面试效果降低? 原因基本是一些公司中,做完功能测试就完了,压根不会有性能测试这一步,或者说并发量较少,没有 ...
- @data注解不生效_面试官:你经常在SpringBoot中使用的条件注解底层是如何实现的?你了解过吗?...
SpringBoot内部提供了特有的注解:条件注解(Conditional Annotation).比如@ConditionalOnBean.@ConditionalOnClass.@Conditio ...
- java 面试题 由浅入深_面试官由浅入深的面试套路
阅读文本大概需要3分钟. 从上图看来面试官面试是有套路的,一不小心就一直被套路. 0x01:Thread 面试官 :创建线程有哪几种方式? 应聘者 :继承Thread类.实现Runable接口.使用j ...
最新文章
- 【NOIP模拟赛】藏宝图 最小生成树
- python怎么返回最初_Python 函数为什么会默认返回 None?
- Chrome 访问一次 Controller,请求却执行两次
- Java split拆分使用竖线为分隔符的字符串方法
- js移除字符串的中文/空格
- apache ranger_Apache Ranger插件的美丽简洁
- 使用HMAC(Play 2.0)保护REST服务
- d3.js 旋转图形_MATLAB 的图形处理
- Canny边缘检測算法原理及其VC实现具体解释(一)
- python互斥锁_python互斥锁
- 什么是向量中断,什么是中断向量?
- java APIs for xml --------dom(2)
- mybatis plus 入门
- Linux重启提示A stop job is running for ...
- win10+VS2013+OPENCV如何配置于仕琪人脸检测算法
- ubuntu 18.04 鼠标多功能键绑定键盘按键
- RobotStudio 示教器编程:MoveC指令
- TCA9548的控制
- 前端面试题汇总(含答案)(JS篇)
- linux 如何关闭屏幕录像,分享|GNOME 有一个“隐藏”的屏幕录像机
热门文章
- 计算器百分号如何用代码实现_如何用 100 行 Python 代码实现新闻爬虫?这样可算成功?...
- windows安装zabbix客户端
- 简述数据在OSI参考模型中的流动过程及过程中数据的单位
- java画布颜色切换_在本视频讲解演示中,扩展画布的目的是为了后面制作齿孔时操作起来方便,扩展部分更换了另一种颜色,是为了以示区别,能直观区分出票面部分。...
- 谈一类神奇的数据结构——猫树
- dynamic programming 学习
- 5月21 回话控制SESSION COOKIE
- (转)5个Xcode开发调试技巧
- win7下vs2008如何进行注册?
- php 源文件加密工具PHP Screw