Java并发数据结构
1,并发数据结构介绍
常用的数据结构是线程不安全的,如平时用到的最多的数据结构类ArrayList、HashMap,HashSet都是**非同步(线程不安全)的,多个线程同时读写,可能会抛出异常或数据错误;传统的Vector、Hashtable等同步(线程安全)**集合性能过差。
从Java5开始,提出了java.util.concurrent这个包,也相应地提供了一些并发数据结构,这些并发的数据结构,将代替以前的这些结构,给并发编程提供更多的支持。这些并发数据结构可以非为以下两大类:
-
阻塞式集合:当集合为空或满时,线程要删除或添加数据时需要等待
-
非阻塞式集合:当集合为空或满时,不等待,返回null或异常
2,List
- Vector:同步安全,适合多线程并发环境下使用,适合写多读少的环境。
- ArrayList:性能比较好,但是它是线程不同步的、不安全的。
- Collections.synchronizedList(List list):基于 synchronized 将一个线程不安全的list变成一个线程安全的list。效率较差。
- CopyOnWriteArrayList:这个类是基于复制机制的并发列表类,它是一个非阻塞式集合,适合读多写少的环境。
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
public class ListTest {
public static void main(String[] args) throws InterruptedException {
// 线程不安全
List<String> unsafeList = new ArrayList<>();
// 线程安全
List<String> safeList1 = Collections.synchronizedList(new ArrayList<>());
// 线程安全
CopyOnWriteArrayList<String> safeList2 = new CopyOnWriteArrayList<>();
ListThread t1 = new ListThread(unsafeList);
ListThread t2 = new ListThread(safeList1);
ListThread t3 = new ListThread(safeList2);
// 分别启动10个t1 t2 t3线程,每个list中将会被插入100条记录
for (int i = 0; i < 10; i++) {
Thread t = new Thread(t1, String.valueOf(i));
t.start();
}
for (int i = 0; i < 10; i++) {
Thread t = new Thread(t2, String.valueOf(i));
t.start();
}
for (int i = 0; i < 10; i++) {
Thread t = new Thread(t3, String.valueOf(i));
t.start();
}
//等待结果
Thread.sleep(2000);
System.out.println("unsafeList.size = " + t1.list.size());
System.out.println("safeList1.size = " + t2.list.size());
System.out.println("safeList2.size = " + t3.list.size());
}
}
class ListThread implements Runnable {
public List<String> list;
public ListThread(List<String> list) {
this.list = list;
}
@Override
public void run() {
int i = 0;
while (i < 10) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 把当前线程名加入list中
list.add(Thread.currentThread().getName());
i++;
}
}
}
![](https://img-blog.csdnimg.cn/20210529173712967.png?size_16,color_FFFFFF,t_70#pic_center)
从结果可以看到,普通的ArrayList难以承受高并发编程的大量读写。
3,Set
- HashSet:线程不安全,不适合再多线程环境下使用。
- Collections.synchronizedSet(Set set):基于 synchronized,效率差。
- CopyOnWriteArraySet:基于CopyOnWriteArrayList实现,它是一个非阻塞式集合,适合读多写少的环境。
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
public class SetTest {
public static void main(String[] args) throws InterruptedException {
// 线程不安全
Set<String> unsafeSet = new HashSet<>();
// 线程安全
Set<String> safeSet1 = Collections.synchronizedSet(new HashSet<>());
// 线程安全
CopyOnWriteArraySet<String> safeSet2 = new CopyOnWriteArraySet<>();
SetThread t1 = new SetThread(unsafeSet);
SetThread t2 = new SetThread(safeSet1);
SetThread t3 = new SetThread(safeSet2);
// 分别启动10个t1 t2 t3线程,每个list中将会被插入100条记录
for (int i = 0; i < 10; i++) {
Thread t = new Thread(t1, String.valueOf(i));
t.start();
}
for (int i = 0; i < 10; i++) {
Thread t = new Thread(t2, String.valueOf(i));
t.start();
}
for (int i = 0; i < 10; i++) {
Thread t = new Thread(t3, String.valueOf(i));
t.start();
}
Thread.sleep(2000);
System.out.println("unsafeSet.size = " + t1.set.size());
System.out.println("safeSet1.size = " + t2.set.size());
System.out.println("safeSet2.size = " + t3.set.size());
}
}
class SetThread implements Runnable {
public Set<String> set;
public ListThread(Set<String> set) {
this.set = set;
}
@Override
public void run() {
int i = 0;
while (i < 100) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 把当前线程名加入set中
set.add(Thread.currentThread().getName() + i);
i++;
}
}
}
![](https://img-blog.csdnimg.cn/20210529173738499.png?size_16,color_FFFFFF,t_70#pic_center)
由于测试的数据量比较小,HashSet的并发读写可能不会出现差错,可以增加测试数据,多测几次。
4,Map
- HashTable:同步安全,写多读少,性能不大好。
- HashMap:性能好,但是不安全。
- Collections.synchronizedMap(Map map):基于 synchronized,效率差。
- ConcurrentHashMap:读多写少,非阻塞式集合。
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
public class MapTest {
public static void main(String[] args) throws InterruptedException {
// 线程不安全
Map<Integer, String> unsafeMap = new HashMap<>();
// 线程安全
Map<Integer, String> safeMap1 = Collections.synchronizedMap(new HashMap<>());
// 线程安全
ConcurrentHashMap<Integer, String> safeMap2 = new ConcurrentHashMap<>();
MapThread t1 = new MapThread(unsafeMap);
MapThread t2 = new MapThread(safeMap1);
MapThread t3 = new MapThread(safeMap2);
// 分别启动10个t1 t2 t3线程,每个map中将会被插入100条记录
for (int i = 0; i < 10; i++) {
Thread t = new Thread(t1, String.valueOf(i));
t.start();
}
for (int i = 0; i < 10; i++) {
Thread t = new Thread(t2, String.valueOf(i));
t.start();
}
for (int i = 0; i < 10; i++) {
Thread t = new Thread(t3, String.valueOf(i));
t.start();
}
Thread.sleep(2000);
System.out.println("unsafeMap.size = " + t1.map.size());
System.out.println("safeMap1.size = " + t2.map.size());
System.out.println("safeMap2.size = " + t3.map.size());
}
}
class MapThread implements Runnable {
public Map<Integer, String> map;
public ListThread(Map<Integer, String> map) {
this.map = map;
}
@Override
public void run() {
int i = 0;
while (i < 100) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 把当前线程名加入set中
map.put(i, Thread.currentThread().getName());
i++;
}
}
}
![](https://img-blog.csdnimg.cn/20210529173757547.png?size_16,color_FFFFFF,t_70#pic_center)
5,Queue & Deque
- ConcurrentLinkedQueue:非阻塞,安全
- ArrayBlockingQueue/LinkedBlockingQueue:阻塞,安全。
import java.util.*;
import java.util.concurrent.*;
public class QueueTest {
public static void main(String[] args) throws InterruptedException {
// 线程不安全
Deque<String> unsafeQueue = new ArrayDeque<>();
// 线程安全
ConcurrentLinkedDeque<String> safeQueue1 = new ConcurrentLinkedDeque<>();
// 线程安全
ArrayBlockingQueue<String> safeQueue2 = new ArrayBlockingQueue<String>(100);
QueueThread t1 = new QueueThread(unsafeQueue);
QueueThread t2 = new QueueThread(safeQueue1);
QueueThread t3 = new QueueThread(safeQueue2);
// 分别启动10个t1 t2 t3线程,每个list中将会被插入100条记录
for (int i = 0; i < 10; i++) {
Thread t = new Thread(t1, String.valueOf(i));
t.start();
}
for (int i = 0; i < 10; i++) {
Thread t = new Thread(t2, String.valueOf(i));
t.start();
}
for (int i = 0; i < 10; i++) {
Thread t = new Thread(t3, String.valueOf(i));
t.start();
}
Thread.sleep(2000);
System.out.println("unsafeQueue.size = " + t1.queue.size());
System.out.println("safeQueue1.size = " + t2.queue.size());
System.out.println("safeQueue2.size = " + t3.queue.size());
}
}
class QueueThread implements Runnable {
public Queue<String> queue;
public QueueThread(Queue<String> queue) {
this.queue = queue;
}
@Override
public void run() {
int i = 0;
while (i < 10) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 把当前线程名加入queue中
queue.add(Thread.currentThread().getName());
i++;
}
}
}
![](https://img-blog.csdnimg.cn/20210529173811163.png?size_16,color_FFFFFF,t_70#pic_center)
在多线程编程中,采用并发的数据结构,可以避免同时读写引发的数据不一致问题,提高数据结构的使用效率。