JAVA多线程提高十三:同步集合类的应用
1.引言
在多线程的环境中,如果想要使用容器类,就需要注意所使用的容器类是否是线程安全的。在最早开始,人们一般都在使用同步容器(Vector,HashTable),其基本的原理,就是针对容器的每一个操作,都添加synchronized来进行同步,此种方式尽管简单,但是其性能是非常地下的,所以现在已经不怎么使用了。人们普遍会使用并发的容器,在JDK1.5之后,针对基于散列的Map,提供了新的ConcurrentHashMap,针对迭代需求的list,提供了CopyOnWriteList.
2.ConcurrentHashMap
ConcurrentHashMap使用了一种分段锁的策略,使得map可以被多个读写线程并行的访问。基本可以认为是将map的key值范围分为多个段,这样多个线程访问的时候,他们需要访问的key值在不同的段,所以可以互相不干扰,
使用不同的锁对象来进行并发操作。
ConcurrentHashMap在使用迭代器遍历的时候,不会报ConcurrentModificationException,提供“弱一致性”。在遍历迭代的时候,也会反应出在迭代器创建之后的数据修改。
应用场景
针对一般的有并发需求的map,都应该使用ConcurrentHashMap. 它的性能优于Hashtable和synchronizedMap。
缺点
不是强一致性
由于是采用的分段锁策略,所以一些数据不能保证强一致性。比如针对容器的size方法,由于线程A只是获得了自己的分段锁,它不能保证其他线程对容器的修改,所以此时线程A可能使用size,会得到不稳定数据。这种情况下,是对同步性能的一些折衷。如果业务需求必须满足强一致性,才会需要对整个Map进行锁操作。并发容器的弱一致性的概念背景,是在高并发情况下,容器的size和isEmpty之类的方法,用处不大,所以可以忍受数据不一致性。
3.CopyOnWrite容器
在JDK1.5之后,java.util.concurrent引入了两个CopyOnWrite容器,分别是CopyOnWriteArrayList, CopyOnWriteArraySet.
顾名思义,CopyOnWrite就是在write操作之前,对集合进行Copy,针对容器的任意改操作(add,set,remove之类),都是在容器的副本上进行的。并且在修改完之后,将原容器的引用指向修改后的副本。
如果线程A得到容器list1的iterator之后,线程B对容器list1加入了新的元素,由于线程A获得list1的iterator时候在线程B对list1进行修改前,所以线程A是看不到线程B对list1进行的任何修改。
具体到源码,看一下add操作
/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return {@code true} (as specified by {@link Collection#add})
*/
public boolean add(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
可以发现,写操作是会有个锁lock.lock(),这保证了多线程写操作之间的同步。之后使用Arrays.copyOf来进行数组拷贝,在修改完成后,setArray(newElements)将原来的数组引用指向新的数组。
应用场景
经常用在读多写少的场景,比如EventListener的添加,网站的category列表等偶尔修改,但是需要大量读取的情景。
缺点
1.数据一致性的问题。
因为读操作没有用到并发控制,所以可能某个线程读到的数据不是实时数据。
2.内存占用问题。
因为写操作会进行数据拷贝,并且旧有的数据引用也可能被其他线程占有一段时间,这样针对数据比较大的情况,可能会占用相当大的内存。并且由于每次写操作都会占用额外的内存,最后进行的GC时间也可能相应的增加。
4.HashSet
HashSet内部是用的HashMap,只用了HashMap的key。
public class User implements Cloneable{
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public boolean equals(Object obj) {
if(this == obj) {
return true;
}
if(!(obj instanceof User)) {
return false;
}
User user = (User)obj;
//if(this.name==user.name && this.age==user.age)
if(this.name.equals(user.name)
&& this.age==user.age) {
return true;
}
else {
return false;
}
}
public int hashCode() {
return name.hashCode() + age;
}
public String toString() {
return "{name:'" + name + "',age:" + age + "}";
}
public Object clone() {
Object object = null;
try {
object = super.clone();
} catch (CloneNotSupportedException e) {}
return object;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
}
JAVA多线程提高十三:同步集合类的应用的更多相关文章
- JAVA多线程提高十:同步工具CyclicBarrier与CountDownLatch
今天继续学习其它的同步工具:CyclicBarrier与CountDownLatch 一.CyclicBarrier CyclicBarrier是一个同步辅助类,它允许一组线程互相等待,直到到达某个公 ...
- JAVA多线程提高十一:同步工具Exchanger
Exchanger可以在对中对元素进行配对和交换的线程的同步点.每个线程将条目上的某个方法呈现给 exchange 方法,与伙伴线程进行匹配,并且在返回时接收其伙伴的对象.Exchanger 可能被视 ...
- JAVA多线程学习十三 - 同步工具CyclicBarrier与CountDownLatch
一.CyclicBarrier CyclicBarrier是一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point).在涉及一组固定大小的线程的程序 ...
- 关于Java多线程的线程同步和线程通信的一些小问题(顺便分享几篇高质量的博文)
Java多线程的线程同步和线程通信的一些小问题(顺便分享几篇质量高的博文) 前言:在学习多线程时,遇到了一些问题,这里我将这些问题都分享出来,同时也分享了几篇其他博客主的博客,并且将我个人的理解也分享 ...
- “全栈2019”Java多线程第二十三章:活锁(Livelock)详解
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...
- “全栈2019”Java多线程第十三章:线程组ThreadGroup详解
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...
- Java多线程-线程的同步(同步方法)
线程的同步是保证多线程安全访问竞争资源的一种手段.线程的同步是Java多线程编程的难点,往往开发者搞不清楚什么是竞争资源.什么时候需要考虑同步,怎么同步等等问题,当然,这些问题没有很明确的答案,但有些 ...
- JAVA多线程提高九:Semaphere同步工具
java 中Semaphere可类比操作系统信号量,硬件资源如IO.内存.磁盘等都是有固定量的,多个程序需要竞争这些资源,没有资源就需要被挂起. 一.类和方法摘要 构造函数: public Semap ...
- Java多线程与线程同步
六.多线程,线程,同步 ①概念: 并行:指两个或多个在时间同一时刻发生(同时发生) 并发:指两个或多个事件在同一时间段内发生 具体概念: 在操作系统中,安装了多个程序,并发指的是在一段时间内宏观上有多 ...
随机推荐
- 团队作业4——第一次项目冲刺(Alpha版本)第一次
一.会议内容 制定任务内容 制作leangoo表格 初步工作 二.各人工作 成员 计划任务 遇见难题 贡献比 塗家瑜(组长) 后端与数据库通讯 无 1 张新磊 表设计 无 1 姚燕彬 测试计划编写 无 ...
- tomcat配置服务
1.在server中右键添加tomcat 2.双击tomcat打开配置窗口添加jvm参数 -Doapath="C:\exeye-workspace\exEyeWeb\oadoc" ...
- 02_Java基础_第2天(变量、运算符)_讲义
今日内容介绍 1.变量 2.运算符 01变量概述 * A: 什么是变量? * a: 变量是一个内存中的小盒子(小容器),容器是什么?生活中也有很多容器, * 例如水杯是容器,用来装载水:你家里的大衣柜 ...
- Java 异常注意事项
异常的注意事项: 1,子类在覆盖父类方法时,父类的方法如果抛出了异常, 那么子类的方法只能抛出父类的异常或者该异常的子类. 2,如果父类抛出多个异常,那么子类只能抛出父类异常的子集. ...
- Spark Shuffle之Sort Shuffle
源文件放在github,随着理解的深入,不断更新,如有谬误之处,欢迎指正.原文链接https://github.com/jacksu/utils4s/blob/master/spark-knowled ...
- QJsonDocument实现Qt下JSON文档读写
版权声明:若无来源注明,Techie亮博客文章均为原创. 转载请以链接形式标明本文标题和地址: 本文标题:QJsonDocument实现Qt下JSON文档读写 本文地址:http://tech ...
- HDU 2105 The Center of Gravity
http://acm.hdu.edu.cn/showproblem.php?pid=2105 Problem Description Everyone know the story that how ...
- python接口自动化测试框架实现之操作mysq数据库
python操作mysql数据库需要使用到mysqlclient库. 安装:pip install mysqlclient python连接mysql数据库分以下步骤: 1.与mysql建立连接: 2 ...
- jdbc 小结
1,PreparedStatement/Statement区别: 1,防止sql注入式攻击(sql注入:就是通过非正常手段(比如在url中添加参数)),将sql文执行(比如or 1=1) 2,Prep ...
- 解决 Package test is missing dependencies for the following libraries: libcrypto.so.1.0.0
根据项目要求需要用到openssl这个库,看了看编译环境幸好本身就集成了该库.但在编译openssl的功能时,碰到缺少类库的错误. Package test is missing dependenci ...