Java同步容器与并发容器

1、Java集合容器分类

在Java的集合容器框架中,主要有五大类别:Set、List、Queue、Map、Deque、Map。
其中Set、List、Queue、Map、Deque接口分别继承了Collection,Collection和Map是一个顶层接口。
Java容器类
Set:
1. 不允许重复对象
2. 无序容器(但LinkedHashset : 保证元素添加顺序、TreeSet : 保证元素的自然顺序)
3. 只允许一个 null 元素
4. 不可以对索引进行操作

List:
1. 允许重复的对象
2. 有序容器,保持了每个元素的插入顺序,输出的顺序就是插入的顺序
3. 可以插入多个null元素
4. 可以对索引进行操作

Queue:
队列(queue)是一种常用的数据结构,可以将队列看做是一种特殊的线性表,该结构遵循的先进先出原则。Java中,LinkedList实现了Queue接口,因为LinkedList进行插入、删除操作效率较高。
常用方法:
offer(e):将元素追加到队列末尾,若添加成功则返回true。
poll():从队首删除并返回该元素。
peek():返回队首元素,但是不删除 。

Deque:
双向队列(Deque),Deque extends Queue , 是Queue的一个子接口,双向队列是指该队列两端的元素既能入队(offer)也能出队(poll);
如果将Deque限制为只能从一端入队和出队,则可实现栈的数据结构。对于栈而言,有入栈(push)和出栈(pop),遵循先进后出原则。
常用方法:
双端队列:在头部、尾部进行插入、删除、获取元素,和 Queue类似;
add(e)\offer(e):将元素追加到队列末尾,若添加成功则返回true。
remove()\poll():从队首删除并返回该元素。
element()\peek():返回队首元素,但是不删除 。
栈:这时入栈、出栈元素都是在 双端队列的头部 进行。
push(e):入栈。
pop():出栈。
peek():返回队首元素,但是不删除 。

Map:

  1. Map不是collection的子接口或者实现类,Map本身是一个接口。
  2. Map 的 每个 Entry 都持有两个对象,也就是一个键一个值,Map 可能会持有相同的值对象但键对象必须是唯一的。
  3. TreeMap 也通过 Comparator 或者 Comparable 维护了一个排序顺序。
  4. Map 里你可以拥有随意个 null 值但最多只能有一个 null 键。(Hashtable不能有null键值)


2、同步容器类

同步容器可以简单地理解为通过synchronized来实现同步的容器,如果有多个线程调用同步容器的方法,它们将会串行执行.

常见的同步容器类
1、Vector(implements List),Hashtable(implements Map),Stack(extends Vector)。
2、Collections.synchronizedXxxx。

  • Collectinons.synchronizedList()
  • Collections.synchronizedSet()
  • Collections.synchronizedSortedSet()
  • Collections.synchronizedSortedMap()

存在的问题
1、同步容器在单线程的环境下能够保证线程安全,但是通过synchronized同步方法将访问操作串行化,导致并发环境下效率低下。而且同步容器在多线程环境下的复合操作(迭代、条件运算如没有则添加等)是非线程安全,需要客户端代码来实现加锁。

public static Object getLast(Vector list) {  
    int lastIndex = list.size() - 1;  
    return list.get(lastIndex);  
}  
public static void deleteLast(Vector list) {  
    int lastIndex = list.size() - 1;  
    list.remove(lastIndex);  
}  

在多线程的情况,一个线程在调用getLast,同时另一个线程在调用deleteLast将元素删除,此时getLast将抛出越界异常。
可以在客户端通过获取容器类的锁,使得两个操作成为原子操作。虽然可以避免之前的问题,但是这也降低了并发的效率,而仅仅实现了同步。

public static Object getLast(Vector list) {
  synchronized(list){
      int lastIndex = list.size() - 1;  
    return list.get(lastIndex);  
}  
}
public static void deleteLast(Vector list) {  
  synchronized(list){
    int lastIndex = list.size() - 1;  
    list.remove(lastIndex);  
}  
}

2、迭代器与ConcurrentModificationException异常
因为“fail-fast”,当容器在迭代过程中被修改了,就会抛出ConcurrentModificationException异常。
fail-fast 机制是java集合(Collection)中的一种错误机制。当多个线程对同一个集合的内容进行操作时,就可能会产生fail-fast事件。
例如:当某一个线程A通过iterator去遍历某集合的过程中,若该集合的内容被其他线程所改变了;那么线程A访问集合时,就会抛出ConcurrentModificationException异常,产生fail-fast事件。
解决办法就是整个迭代过程持有容器的锁,但是这这又很大程度降低并发效率。迭代规模很大时,就是有很多线程被阻塞等待锁。


3、并发容器

同步容器并不能保证多线程安全,并发容器是为多线程并发访问而设计的,在jdk5引入了concurrent包,其中提供了很多并发容器,极大的提升同步容器类的性能。

常见的并发容器:
- Map—>ConcurrentHashMap
- SortedMap—>ConcurrentSkipListMap
- SortedSet—>ConcurrentSkipListSet
- List—>CopyOnWriteArrayList
- Queue—>ConcurrentLinkedQueue

猜你喜欢

转载自blog.csdn.net/itcats_cn/article/details/81330295
今日推荐