编程中,我们经常需要集中存放多个数据。数组是我们的一个很好的选择,前提是我们事先明确我们将要保存对象的数量。数组在初始化时如果指定了长度,那这个数组长度就是不可变的了,如果我们需要保存一个可以动态增长的数据(编译时无法确定具体的对象数量),所以Java提供了集合框架来实现这个功能。
集合类主要负责保存数据,因此集合类也被称为容器类。Java中集合类都位于java.util包下,后来为了处理多线程并发安全问题,Java5在java.util.concurrent包下提供了支持多线程并发的集合类。
Java容器类的用途是”保存对象”,并将其划分为两个不同的概念:
Collection和Map的区别在于容器中每个位置保存的元素个数:
Java集合框架Collection图示:
我们着重说明一下Java并发的集合框架,就是java.util.concurrent包下的集合类
非阻塞队列就是在队列中没有数据时,对此队列的操作将出现异常或者返回null,无需等待/阻塞的特色。
在JDK的并发包中,常见的非阻塞队列有以下几个:
- ConcurrentHashMap
- ConcurrentSkipListMap
- ConcurrentSkipListSet
- ConcurrentLinkedQueue
- ConcurrentLinkedDeque
- CopyOnWriteArrayList
- CopyOnWriteArraySet
ConcurrentHashMap类
这个类是支持并发操作的Map对象,与之对应的不支持并发操作的Map对象是HashMap,Hashtable对象也是Map的一个子类,也是支持并发操作,但是其不支持Iterator并发的删除操作,如果程序中要对map集合做并发以及Iterator并发编辑/删除操作,推荐使用ConcurrentHashMap集合类。
HashMap集合不支持并发示例:
package com.collections.hashmapdemo;
import java.util.HashMap;
public class HashMapDemo {
public static HashMap hashMap = new HashMap();
public static void main(String[] args) {
Thread threadA = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 50000; i++) {
hashMap.put("ThreadA" + (i + 1), "ThreadA" + i + 1);
System.out.println("ThreadA" + (i + 1));
}
}
});
Thread threadB = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 50000; i++) {
hashMap.put("ThreadB" + (i + 1), "ThreadB" + i + 1);
System.out.println("ThreadB" + (i + 1));
}
}
});
threadA.start();
threadB.start();
}
}
执行结果
有时候程序可以正常执行完,有时候则程序会造成假死的状态,如下图:
所以说HashMap不支持多线程并发的操作。
Hashtable集合支持并发
示例:
只需要把HashMap示例中public static HashMap hashMap = new HashMap();修改成public static Hashtable hashtable = new Hashtable();即可。
我们就说说Hashtable不支持Iterator的编辑/删除操作,示例如下
package com.collections.hashmapdemo;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.concurrent.TimeUnit;
public class HashtableNotModifyDemo {
public static Hashtable hashtable = new Hashtable();
static {
for (int i = 0; i < 5; i++) {
hashtable.put("String" + (i + 1), i + 1);
}
}
public static void main(String[] args) {
Thread threadA = new Thread(new Runnable() {
@Override
public void run() {
try {
Iterator iterator = hashtable.keySet().iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
TimeUnit.SECONDS.sleep(2);
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
Thread threadB = new Thread(new Runnable() {
@Override
public void run() {
hashtable.put("z", "zValue");
}
});
threadA.start();
threadB.start();
}
}
执行结果
String5
java.util.ConcurrentModificationException
at java.util.Hashtable Enumerator.next(Hashtable.java:1167)atcom.collections.hashmapdemo.HashtableNotModifyDemo 1.run(HashtableNotModifyDemo.java:22)
at java.lang.Thread.run(Thread.java:745)
说明多线程调用该类的iterator()方法返回Iterator对象后,在调用put方法会报ConcurrentModificationException异常,也就是不支持Iterator并发的编辑/删除操作。如果想实现此功能推荐使用并发集合框架提供的ConcurrentHashMap类。
ConcurrentHashMap集合支持并发示例:
需要把HashMap示例中public static HashMap hashMap = new HashMap();修改成public
static ConcurrentHashMap concurretHashMap = new ConcurrentHashMap();即可。
ConcurrentHashMap集合支持Iterator并发操作示例:
把类HashtableNotModifyDemo中的public static Hashtable hashtable = new Hashtable();修改为public static ConcurrentHashMap concurretHashMap = new ConcurrentHashMap();测试即可。
ConcurrentSkipListMap类
这个并发集合类支持排序功能。一般此集合中存放的对象要实现Comparable接口,方便此集合类排序,如果不实现此接口,则会按照默认排序。
ConcurrentSkipListSet类
这个并发集合类不仅支持排序功能,还不允许重复的元素。一般此集合中存放的对象要实现Comparable接口,并且重写equals和hashCode方法。
ConcurrentLinkedQueue类
这个并发集合类提供了并发环境的队列操作。
ConcurrentLinkedDeque类
ConcurrentLinkedQueue支持对队列头操作,而ConcurrentLinkedDeque也支持队列头和列尾双向操作。
CopyOnWriteArrayList类
ArrayList是线程不安全的,如果想在并发中实现线程安全,则可以使用CopyOnWriteArrayList类,使用方法和ArrayList无异。
CopyOnWriteArraySet类
ArraySet是线程不安全的,如果想在并发中实现线程安全,则可以使用CopyOnWriteArraySet类,使用方法和ArraySet无异。
to be continue……