一 同步类容器
同步类容器都是线程安全的,但在某些场景中可能需要加锁来保证复合操作。
符合操作如:迭代(反复访问元素,遍历完容器中所有元素)、跳转(根据指定的顺序找到当前元素的下一个元素)、条件运算。这些复合操作在多线程并发地修改容器时,可能会表现出意外的行为,最经典的ConcurrentModificationException,原因是当前容器迭代的过程中,被并发的修改了内容,这是由于早期迭代器设计的时候没有考虑到并发修改。

同步类容器:如古老的Vector、HashTable。这些容器的同步功能其实都是有JDK的Collections.synchronized***等工厂方法去创建实现的。其底层的机制无非就是用传统的synchronized关键字对每个共用方法都进行同步,使得每次只有一个线程访问容器的状态。这不符合今天互联网高并发的需求。
 二 并发类容器
jdk1.5之后,提供了多种并发类容器来替代同步类容器,从而改善性能。同步类容器都是串行化的,它们虽然实现了线程安全,但是严重降低了并发性,在多线程环境时,严重降低了性能。

并发类容器是专门针对并发设计的,使用ConcurrentHashMap来替代给予散列的传统的HashTable,而且在ConcurrentHashMap中,添加了一些常见复合操作的支持。以及使用CopyOnWriteArrayList替代Vector,并发的CopyOnWriteArraySet,以及并发的Queue,ConcurrentLinkedQueue和LinkedBlockingQueue,前者是高性能队列,后者是以阻塞形式的队列,具体实现Queue还有很多,例如ArrayBlockingQueue、SynchronousQueue、PriorityBlockingQueue等。
 2.1 ConcurrentMap
ConcurrentMap接口下两个重要实现类:ConcurrentHashMap、ConcurrentSkipListMap(支持并发排序功能,弥补ConcurrentHashMap)
ConcurrentHashMap:内部使用段(Segment)来表示这些不同的部分,每个段其实就是一个小的HashTable,他们有自己的锁。只要多个修改操作发生在不同的段上,它们就可以并发进行。把一个整体分成了16个段,也就是最高支持16个线程的并发修改操作。这也是在多线程场景时减小锁的粒度从而降低锁竞争的一种方案,并且代码中大多共享变量使用volatile关键字声明,目的是第一时间获取修改的内容,性能非常好。
2.2 CopyOnWrite容器
Copy-On-Write简称COW,是一种用于程序设计中的优化策略。
JDK中的COW容器有两种:CopyOnWriteArrayList、CopyOnWriteArraySet
CopyOnWrite容器即写时复制的容器。通俗的理解是当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加元素,添加完后,再将原容器的引用指向新的容器。这样做的好处是我们可以对CopyOnWrite容器进行并发的读,而且不需要加锁,因为当前容器不会添加任何元素。所以CopyOnWrite容器也是一种读写分离的思想,读和写不同容器。场景:读多写少
三 并发Queue
并发==队列先进先出==,有两套实现:ConcurrentLinkedQueue接口高性能队列、BlockingQueue接口为代表的阻塞队列
 3.1 ConcurrentLinkedQueue类
ConcurrentLinkedQueue:是一个适用于高并发场景下的队列,通过无锁的方式,实现了高并发状态下的高性能,通常ConcurrentLinkedQueue性能好于BlockingQueue,它是一个基于链接节点的无界线程安全队列。该队列的元素遵循先进先出的原则,头是最先加入的,尾是最近加入的,该队列不允许null元素。
ConcurrentLinkedQueue队列的重要方法:
1. add()和offer():都是加入元素的方法
2. poll()、take()和peek():都是取头元素节点,区别在于前者会删除元素,后者不会

3.1 BlockingQueue接口
1. ArrayBlockingQueue:基于数组实现的阻塞队列实现,在ArrayBlockingQueue内部,维护了一个定长数组,以便缓存队列中的数据对象,其内部==没实现读写分离==,也就意味着生产和消费不能完全同步并行,长度是需要定义的,可以指定先进先出或者后进后出,也叫==有界队列==,在很多场合非常适用
2. LinkedBlockingQueue:基于链表的阻塞队列,与ArrayBlockingQueue类似,其内部也维持着一个数据缓冲队列(该队列由一个链表构成),LinkedBlockingQueue之所以能够高效的处理并发数据,是因为其内部实现采用分离锁(==读写分离==两个锁),从而实现生产者和消费者并行,是一个==无界队列==
3. SynchronousQueue:一种==没有缓冲==的队列,生产者生产的数据直接会被消费者获取并消费。不能添加,元素先take()取出阻塞,当有元素添加,后才取出

```
BlockingQueue<String> synchronousQueue = new SynchronousQueue<>();
synchronousQueue.add("a");
```

> 运行结果:
Exception in thread "main" java.lang.IllegalStateException: Queue full
at java.util.AbstractQueue.add(AbstractQueue.java:98)
at com.zys.test.test.StringTest.main(StringTest.java:13)

```
public class SynQueue { public static void main(String[] args) {
SynchronousQueue<String> queue = new SynchronousQueue<>();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println("-----" + queue.take() + "\n");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
},"t1");
t1.start();
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
queue.add("aaaa");//先take()取出阻塞,当有元素添加,后才取出
queue.add("bbbb");
}
},"t2");
t2.start();
}
}
``` 

> 运行结果:
-----aaaa
Exception in thread "t2"
java.lang.IllegalStateException: Queue full
at java.util.AbstractQueue.add(AbstractQueue.java:98)
at Queue.SynQueue$2.run(SynQueue.java:25)
at java.lang.Thread.run(Thread.java:745)

4. PriorityBlockingQueue:基于优先级的阻塞队列(优先级的判断通过构造函数传入的Compator对象来决定,也就是说传入队列的对象必须实现Comparable接口),在实现PriorityBlockingQueue时,内部控制线程同步的锁采用的公平锁,也是一个无界队列

public class Task implements Comparable<Task>{

	private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public int compareTo(Task o) {
return this.id > o.id ? 1 : (this.id < o.id ? -1 : 0);
}
@Override
public String toString() {
return this.id + "," + this.name;
}
} public class UsePriorityBlockingQueue { public static void main(String[] args) {
PriorityBlockingQueue<Task> queue = new PriorityBlockingQueue<>();
Task t1 = new Task();
t1.setId(3);
t1.setName("task1");
Task t2 = new Task();
t2.setId(6);
t2.setName("task2");
Task t3 = new Task();
t3.setId(1);
t3.setName("task3");
queue.add(t1);
queue.add(t2);
queue.add(t3);
System.out.println(queue);
try {
System.out.println(queue.peek());//peek和element取出但不移除
System.out.println(queue.take());//take、poll和remove取出且移除
//优先级排序,并返回优先级最高的元素,并在队列中删除取出的元素
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(queue);
for (Iterator<Task> iter = queue.iterator() ; iter.hasNext();) {
Task task = iter.next();
System.out.println(task.getName());
}
}
}

> 运行结果:
[1,task3, 6,task2, 3,task1]
1,task3
1,task3
[3,task1, 6,task2]
task1
task2
特点:添加元素时不排序,在取值时根据优先级排序

5. DelayQueue:带有延迟时间的Queue,其中的元素只有当其指定的==延迟时间==到了,才能够从队列中获取到该元素。DelayQueue中的元素必须实现Delayed接口,DelayQueue是一个没有大小限制的队列,应用场景很多,比如对缓存超时的数据进行移除、任务超时处理、空闲连接的关闭等等。

public class Wangmin implements Delayed{

	private String id;
private String name;
private long endTime;
private TimeUnit timeUnit = TimeUnit.SECONDS; //设定单位为秒 public Wangmin(String id, String name, long endTime) {
super();
this.id = id;
this.name = name;
this.endTime = endTime;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public long getEndTime() {
return endTime;
}
public void setEndTime(long endTime) {
this.endTime = endTime;
} /**
* 上网时间相互比较,确定排序
*/
@Override
public int compareTo(Delayed o) {
Wangmin min = (Wangmin)o;
return this.getDelay(this.timeUnit) - min.getDelay(this.timeUnit) > 0 ? 1 : 0;
}
/**
* 返回延迟时间
*/
@Override
public long getDelay(TimeUnit unit) {
return this.endTime - System.currentTimeMillis();
} } public class WangBa implements Runnable{ private DelayQueue<Wangmin> delayQueue = new DelayQueue<Wangmin>();
public boolean yinye = true; public void shangji(String name, String id, int money) {
Wangmin min = new Wangmin(id, name, money * 1000 + System.currentTimeMillis());
System.out.println("网民:" + min.getName() + ",身份证号码为:" + min.getId() + ",网费:" + money + "\t开始上网,上网结束时间为:" + min.getEndTime());
this.delayQueue.add(min);
} public void xiaji(Wangmin min) {
System.out.println("网民:" + min.getName() + "下机时间到:" + System.currentTimeMillis());
}
@Override
public void run() {
while(yinye) {
try {
Wangmin min = delayQueue.take();
xiaji(min);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} public static void main(String[] args) {
System.out.println("网吧开始营业...");
WangBa wangBa = new WangBa();
Thread shangwang = new Thread(wangBa);
shangwang.start();
wangBa.shangji("路人甲", "111", 1);
wangBa.shangji("路人乙", "222", 10);
wangBa.shangji("路人丙", "333", 5);
}
}

> 运行结果:
网吧开始营业...
网民:路人甲,身份证号码为:111,网费:1 开始上网,上网结束时间为:1532587709166
网民:路人乙,身份证号码为:222,网费:10 开始上网,上网结束时间为:1532587718167
网民:路人丙,身份证号码为:333,网费:5 开始上网,上网结束时间为:1532587713168
网民:路人甲下机时间到:1532587709166
网民:路人丙下机时间到:1532587713168
网民:路人乙下机时间到:1532587718167

同步类容器和并发类容器——ConcurrentMap、CopyOnWrite、Queue的更多相关文章

  1. Java并发——同步容器与并发容器

    同步容器类 早期版本的JDK提供的同步容器类为Vector和Hashtable,JDK1.2 提供了Collections.synchronizedXxx等工程方法,将普通的容器继续包装.对每个共有方 ...

  2. 并发编程(二)------并发类容器ConcurrentMap

    并发类容器: jdk5.0以后提供了多种并发类容器来替代同步类容器从而改善性能. 同步类容器的状态都是串行化的. 他们虽然实现了线程安全,但是严重降低了并发性,在多线程环境时,严重降低了应用程序的吞吐 ...

  3. Java线程同步类容器和并发容器(四)

    同步类容器都是线程安全的,在某些场景下,需要枷锁保护符合操作,最经典ConcurrentModifiicationException,原因是当容器迭代的过程中,被并发的修改了内容. for (Iter ...

  4. JUC源码分析-集合篇:并发类容器介绍

    JUC源码分析-集合篇:并发类容器介绍 同步类容器是 线程安全 的,如 Vector.HashTable 等容器的同步功能都是由 Collections.synchronizedMap 等工厂方法去创 ...

  5. 【Java并发工具类】Java并发容器

    前言 Java并发包有很大一部分都是关于并发容器的.Java在5.0版本之前线程安全的容器称之为同步容器.同步容器实现线程安全的方式:是将每个公有方法都使用synchronized修饰,保证每次只有一 ...

  6. 并发编程(三)------并发类容器Copy-On-Write容器

    Copy-On-Write简称COW,是一种用于程序设计中的优化策略.JDK里的COW容器有两种: CopyOnWriteArrayList CopyOnWriteArraySet CopyOnWri ...

  7. java多线程总结-同步容器与并发容器的对比与介绍

    1 容器集简单介绍 java.util包下面的容器集主要有两种,一种是Collection接口下面的List和Set,一种是Map, 大致结构如下: Collection List LinkedLis ...

  8. JAVA同步容器和并发容器

    同步容器类 同步容器类的创建 在早期的JDK中,有两种现成的实现,Vector和Hashtable,可以直接new对象获取: 在JDK1.2中,引入了同步封装类,可以由Collections.sync ...

  9. Java 同步容器和并发容器

      同步容器(在并发下进行迭代的读和写时并不是线程安全的)   Vector.Stack.HashTable   Collections类的静态工厂方法创建的类(如Collections.synchr ...

随机推荐

  1. c++后台开发面试常见知识点总结(四)数据库

    数据库的索引类型 聚集索引和非聚集索引的区别(叶节点存储内容) 唯一性索引和主码索引的区别 索引的优缺点,什么时候使用索引,什么时候不能使用索引(重点) 索引最左前缀问题 数据库中事务的ACID 数据 ...

  2. Python--模块之sys模块、logging模块、序列化json模块、序列化pickle模块

    sys模块 sys.argv 命令行参数List,第一个元素是程序本身路径 sys.exit(n) 退出程序,正常退出时exit() sys.path 返回模块的搜索路径,初始化时使用PYTHONPA ...

  3. tcgetattr学习

    一.函数名称: int tcgetattr(int fd, struct termios *termios_p); 二.函数功能: The termios functions describe a g ...

  4. Redis入门很简单之五【Jedis和Spring的整合】

    Redis入门很简单之五[Jedis和Spring的整合] 博客分类: NoSQL/Redis/MongoDB redisnosql缓存jedisspring  在上一篇文章中,简单介绍了Jedis的 ...

  5. HTML5: HTML5 内联 SVG

    ylbtech-HTML5: HTML5 内联 SVG 1.返回顶部 1. HTML5 内联 SVG HTML5 支持内联 SVG. 什么是SVG? SVG 指可伸缩矢量图形 (Scalable Ve ...

  6. cs224d 作业 problem set2 (二) TensorFlow 实现命名实体识别

    神经网络在命名实体识别中的应用 所有的这些包括之前的两篇都可以通过tensorflow 模型的托管部署到 google cloud 上面,发布成restful接口,从而与任何的ERP,CRM系统集成. ...

  7. echarts数据包坐标拾取工具

    http://geojson.io/#map=4/37.20/103.45

  8. .net报错大全

    1.未能加载文件或程序集“Newtonsoft.Json, Version=4.5.0.0"[已解决] 解决方案:https://blog.csdn.net/mzl87/article/de ...

  9. Java语言基础及java核心

    一.Java语言特点 1. 简单 2. 面向对象 3. 分布式 4. 健壮 5. 安全 6. 中性架构跨平台 7. 超强的可移植性 8. 高性能 9. 多线程 二.java的环境变量 JAVA_HOM ...

  10. docker volume持久化存储与数据分享

    第一种 指定volume文件mysql存储,存储的位置为/var/lib/mysql -v mysql:/var/lib/mysql 第二种 同步文件,将容器中的skeleton文件夹的内容同步到宿主 ...