java集合 线程安全

1.快速失败(fail-fast)和安全失败(fail-safe)?

一:快速失败(fail—fast)java

在用迭代器遍历一个集合对象时,若是遍历过程当中对集合对象的内容进行了修改(增长、删除、修改),则会抛出Concurrent Modification Exception。编程

原理:迭代器在遍历时直接访问集合中的内容,而且在遍历过程当中使用一个 modCount 变量。集合在被遍历期间若是内容发生变化,就会改变modCount的值。每当迭代器使用hashNext()/next()遍历下一个元素以前,都会检测modCount变量是否为expectedmodCount值,是的话就返回遍历;不然抛出异常,终止遍历。安全

注意:这里异常的抛出条件是检测到 modCount!=expectedmodCount 这个条件。若是集合发生变化时修改modCount值恰好又设置为了expectedmodCount值,则异常不会抛出。所以,不能依赖于这个异常是否抛出而进行并发操做的编程,这个异常只建议用于检测并发修改的bug。多线程

场景:java.util包下的集合类都是快速失败的,不能在多线程下发生并发修改(迭代过程当中被修改)。并发

二:安全失败(fail—safe)框架

采用安全失败机制的集合容器,在遍历时不是直接在集合内容上访问的,而是先复制原有集合内容,在拷贝的集合上进行遍历。工具

原理:因为迭代时是对原集合的拷贝进行遍历,因此在遍历过程当中对原集合所做的修改并不能被迭代器检测到,因此不会触发Concurrent Modification Exception。性能

缺点:基于拷贝内容的优势是避免了Concurrent Modification Exception,但一样地,迭代器并不能访问到修改后的内容,即:迭代器遍历的是开始遍历那一刻拿到的集合拷贝,在遍历期间原集合发生的修改迭代器是不知道的。线程

场景:java.util.concurrent包下的容器都是安全失败,能够在多线程下并发使用,并发修改。指针

2.线程安全的java集合,以及其原理 (https://www.imooc.com/article/34600)

jdk1.5以前实现集合的并发开发和jdk1.5版本之后有所差异

jdk1.5前:

  (1)最为简单直接的就是在程序中咱们本身对共享变量进行加锁。不过,缺点也显而易见,手动实现线程安全间接增长了程序的复杂度,以及代码出错的几率---例如:线程死锁的产生;
  (2)咱们还可使用Java集合框架中的Vector、Hashtable实现类,这两个类都是线程安全的。不过,Java已不提倡使用。
  (3)此外,咱们还可使用集合工具类--Collections,经过调用其中的静态方法,来获得线程安全的集合。具体方法,包括:Collections.synchronizedCollection(Collection<T> c)、  Collections.synchronizedSet(Set<T> s)、Collections.synchronizedList(List<T>)、Collections.synchronizedMap(Map<K, V>)。
  究其原理,他们都是经过在方法中加synchronized同步锁来实现的。咱们知道synchronized锁的开销较大,在程序中不建议使用。
  虽然,这三种方式能够实现线程安全的集合,可是都有显而易见的缺点,并且也不是咱们今天所关注的重点。

jdk1.5推出了java.util.concurrent包。该包的出现,让Java并发编程变得更加轻松,帮助开发者编写更加高效、易维护、结构清晰的程序:

  阻塞式集合:当集合已满或为空时,被调用的添加(满)、移除(空)方法就不能当即被执行,调用这个方法的线程将被阻塞,一直等到该方法能够被成功执行。
  非阻塞式集合:当集合已满或为空时,被调用的添加(满)、移除(空)方法就不能当即被执行,调用这个方法的线程不会被阻塞,而是直接则返回null或抛出异常。

下面,就来看下concurrent包下,到底存在了哪些线程安全的集合:
Collection集合:

List:
  CopyOnWriteArrayList
Set:
  CopyOnWriteArraySet
  ConcurrentSkipListSet
Queue:
  BlockingQueue:
  LinkedBlockingQueue
DelayQueue
  PriorityBlockingQueue
  ConcurrentLinkedQueue
TransferQueue:
  LinkedTransferQueue
  BlockingDeque:
  LinkedBlockingDeque
  ConcurrentLinkedDeque
Map集合:

Map:
  ConcurrentMap:
  ConcurrentHashMap
  ConcurrentSkipListMap
  ConcurrentNavigableMap
经过以上能够看出,java.util.concurrent包为每一类集合都提供了线程安全的实现。

其中list又采用的是CopyOnWrite机制,我对其理解是,这个机制与Array不一样的是,其在get()方法中是不加锁的,提高了性能.能够这么作的缘由是,CopyOnWrite在add()的时候,既添加了锁,又拷贝了备份,在备份上操做后又将指针指回原地址.

简单的理解,就是当咱们往CopyOnWrite容器中添加元素时,不直接操做当前容器,而是先将容器进行Copy,而后对Copy出的新容器进行修改,修改后,再将原容器的引用指向新的容器,即完成了整个修改操做;

更详细的解析须要到原博文去阅读

相关文章
相关标签/搜索