上面一节我们介绍了ArrayList、HashSet、HashMap这些容器都是非线程安全的。如果有多个线程并发访问这些容器时,就会触发线程安全问题。因此在编写程序的时候,必须要求开发人员手动的在任何访问到这些容器的地方进行同步处理,这样就导致使用起来非常不便。因此java提供了同步容器方便使用。

在java中同步容器主要包括两类:

  • ArrayList -> Vector,Stack;     HashMap -> HashTable(key,value不能为null)
  • Collections.synchronizedXXX(List, Set, Map)

Vector

Vector实现了List接口,实际上就是一个数组。与ArrayList非常类似。但是Vector中的所有方法都是使用synchronized方法修饰的方法,进行了同步的措施。因此在多线程环境下使用ArrayList对象时,如果被多个线程共享使用可以换成同步的Vector,这样的话线程安全型会更好一些(而不是完全线程安全的)。

@Slf4j
@ThreadSafe
public class VectorExample1 {// 请求总数public static int clientTotal = 5000;// 同时并发执行的线程数public static int threadTotal = 200;private static List<Integer> list = new Vector<>();public static void main(String[] args) throws InterruptedException {//线程池ExecutorService executorService = Executors.newCachedThreadPool();//定义信号量final Semaphore semaphore = new Semaphore(threadTotal);//定义计数器final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);for(int i = 0; i < clientTotal; i++) {final int count  = i;executorService.execute(() ->{try {semaphore.acquire();update(count);semaphore.release();} catch (InterruptedException e) {log.error("exception", e);}countDownLatch.countDown();});}countDownLatch.await();executorService.shutdown();log.info("size:{}",list.size()) ;}public static void update(int i) {list.add(i);}}

这样输出的结果就是预期的结果。

为什么说同步容器不是线程安全的?

@NotThreadSafe
public class VectorExample2 {private static Vector<Integer> vector = new Vector<>();public static void main(String[] args) {while (true){for (int i = 0;i < 10;i++) {vector.add(i);}Thread thread1 = new Thread(){@Overridepublic void run() {for (int i = 0;i < vector.size();i++) {vector.remove(i);}}};Thread thread2 = new Thread(){@Overridepublic void run() {for (int i = 0;i < vector.size();i++) {vector.get(i);}}};thread1.start();thread2.start();}}
}

运行,抛出异常:

Exception in thread "Thread-611" java.lang.ArrayIndexOutOfBoundsException: Array index out of range: 5at java.util.Vector.get(Vector.java:748)at com.vincent.example.syncContainer.VectorExample2$2.run(VectorExample2.java:25)
Exception in thread "Thread-1759" java.lang.ArrayIndexOutOfBoundsException: Array index out of range: 21at java.util.Vector.get(Vector.java:748)at com.vincent.example.syncContainer.VectorExample2$2.run(VectorExample2.java:25)

原因:get发生越界肯定是remove方法引起的,vector虽然能保证同一时刻只能有一个线程能访问他,但是不排除有这种可能:当某个线程某个时刻执行到int i = 0;i < vector.size()时,vector.size()返回10,i=9;而另外一个线程正好将i=9的vector移除掉了,这时get方法想调用i=9的元素就会出现数组越界的异常。

这个例子演示了两个同步容器的两个同步方法因为操作顺序的差异,在不同线程里面可能会触发线程不安全的问题。因此为了保证线程安全,必须在方法调用端做一些额外的同步措施才可以。在使用同步容器时并不是在所有场合下都是线程安全的。

Stack

Stack中的方法也使用了synchronized修饰了,实际上Stack类继承了Vector类。

HashTable

HashTable实现了Map接口,与HashMap很相似,但是HashTable进行了同步处理,方法也是使用了synchronized进行了修饰。但是在使用HashTable时一定要注意key和value是不能为null的。

Collections

将新建ArrayList、HashSet、HashMap对象由Collections产生:

private static List<Integer> list = Collections.synchronizedList(new ArrayList<>());
private static Set<Integer> set = Collections.synchronizedSet(new HashSet<>());
private static Map<Integer, Integer> map = Collections.synchronizedMap(new HashMap<>());

在集合遍历过程中删除操作

public class VectorExample3 {//Exception in thread "main" java.util.ConcurrentModificationExceptionprivate static void test1(Vector<Integer> v1){ //foreachfor(Integer i: v1){if(i.equals(3)){v1.remove(i);}}}//Exception in thread "main" java.util.ConcurrentModificationExceptionprivate static void test2(Vector<Integer> v1){ //iteratorIterator<Integer> integerIterator = v1.iterator();while (integerIterator.hasNext()) {Integer i = integerIterator.next();if(i.equals(3)){v1.remove(i);}}}// successprivate static void test3(Vector<Integer> v1){for(int i = 0; i < v1.size(); i++) {if(v1.equals(3)){v1.remove(i);}}}public static void main(String[] args) {Vector<Integer> vector = new Vector<>();vector.add(1);vector.add(2);vector.add(3);test1(vector);}
}

如果使用了Foreach或者迭代器来循环我们的集合时,尽量不要在循环中做集合的删除操作,如果要做remove操作时,建议在遍历的过程中发现需要删除的值然后做一个标记,在遍历结束后在执行相应的remove操作。在多线程情况下出现异常的情况会更大。

一般情况下我们通常使用并发容器来代替同步容器

java高并发(十一)同步容器相关推荐

  1. Java 高并发_JAVA并发编程与高并发解决方案 JAVA高并发项目实战课程 没有项目经验的朋友不要错过!...

    JAVA并发编程与高并发解决方案 JAVA高并发项目实战课程 没有项目经验的朋友不要错过! 1.JPG (37.82 KB, 下载次数: 0) 2018-12-3 09:40 上传 2.JPG (28 ...

  2. java高并发(一)导学

    现在准备系统学习java高并发与多线程相关知识. 首先了解一下我们这一套知识的学习思路: 并发与高并发相关概念 CPU多级缓存 缓存一致性 乱序执行优化 java内存模型 JMM规定.抽象结构 同步操 ...

  3. Java高并发编程 (马士兵老师视频)笔记(一)同步器

    本篇主要总结同步器的相关例子:包括synchronized.volatile.原子变量类(AtomicXxx).CountDownLatch.ReentrantLock和ThreadLocal.还涉及 ...

  4. JAVA高并发程序设计(葛一鸣著)读书笔记

    本文为JAVA高并发程序设计(葛一鸣著)读书笔记.这本书对于刚刚入门的童鞋来讲可能有点深,我推荐可以先看看Java多线程编程核心技术(高洪岩著)一书. 第一章 走入并行世界 什么是同步和异步? 同步就 ...

  5. Java高并发程序设计入门

    转自:http://blog.csdn.net/johnstrive/article/details/50667557 说在前面 本文绝大部分参考<JAVA高并发程序设计>,类似读书笔记和 ...

  6. Java高并发系列5-线程池

    Java高并发系列5-线程池 接上一篇Java并发系列4-并发容器我们继续 在编程中经常会使用线程来异步处理任务,但是每个线程的创建和销毁都需要一定的开销.如果每次执行一个任务都需要开个新线程去执行, ...

  7. java高并发实际处理简介

    java高并发简介 秒杀锁定图 平时项目中,如果多个客户同时需要修改或者审批同一个业务数据的时候,这个时候我们需要考虑脏数据和数据不可重复读问题.脏数据和数据不可重复读问题是java并发的一种业务场景 ...

  8. 《实战Java高并发程序设计》github笔记和源码

    笔记 <实战Java高并发程序设计>中有很多代码范例,适合初学者通过实践入门并发编程,这本书有个问题就是前面的代码都用JDK7,第六章开始又用JDK8了 笔者做了相关笔记并整理源代码,欢迎 ...

  9. Java 高并发系列1-开篇

    Java 高并发系列1-开篇 我们都知道在Android开发中, 一个Android程序启动之后会有一个主线程,也就是UI线程, 而网络加载数据, 本地文件长时间读写,图片压缩,等等,很多耗时操作会阻 ...

  10. Java高并发,如何解决,什么方式解决

     对于我们开发的网站,如果网站的访问量非常大的话,那么我们就需要考虑相关的并发访问问题了.而并发问题是绝大部分的程序员头疼的问题, 但话又说回来了,既然逃避不掉,那我们就坦然面对吧~今天就让我们一 ...

最新文章

  1. 【多标签文本分类】SGM: Sequence Generation Model for Multi-Label Classification
  2. UVA 617 - Nonstop Travel(数论+暴力枚举)
  3. C++ vector多维数组初始化及清零
  4. 人工智能:第一章 绪 论
  5. linux有关网络服务的接口,linux系统有关网络服务接口定义是哪个?
  6. 从Script到Code Blocks、Code Behind到MVC、MVP、MVVM
  7. 2010年06月12日
  8. 提高网页中GOOGLE广告显示速度的代码
  9. PHP大转盘中奖概率算法实例
  10. 分布式搜索Elasticsearch增、删、改、查操作深入详解
  11. python数据驱动读取用例_Python3-unittest测试框架之DDT数据驱动
  12. 软件项目管理实用教程(人民邮电出版)第一章课后习题
  13. 1. 无穷维空间的测度论-Wiener测度(二)
  14. opencv的core组件——像素,ROI,图像混合(3)
  15. 5. 求cosx计算公式
  16. 什么是计算机图形学?(转自中国科学技术大学-刘利刚)
  17. application.yaml配置详解
  18. 论责任成本管理体系的构建
  19. 苹果计算机如何出现关机界面,让你的iPhone开关机重启和电脑一样显示进度条
  20. 解决一个输出文档的问题

热门文章

  1. GridView多行表头合并
  2. Mysql 常用函数(19)- mod 函数
  3. MYSQL中常用的强制性操作(例如强制索引)
  4. 破解百度网盘的Pandownload开发者被捕,让人唏嘘
  5. python语言中with as的用法使用详解
  6. 使用 monitor 命令查看 redis 请求日志
  7. Win的phpstudy安装VC报错
  8. kass中lisp文件,常见的Lisp-获取文件的路径
  9. mysql表的类型_浅谈MySQL表类型
  10. c语言获取五子棋盘光标位置,跪求C语言五子棋悔棋部分实现