1- # 同步容器和并发容器
1+ # Java 并发容器
22
33> ** 📦 本文以及示例源码已归档在 [ javacore] ( https://github.com/dunwu/javacore ) **
44
55<!-- TOC depthFrom:2 depthTo:3 -->
66
7- - [ 同步容器] ( #同步容器 )
8- - [ 同步容器的缺陷] ( #同步容器的缺陷 )
9- - [ 并发容器] ( #并发容器 )
10- - [ ConcurrentHashMap] ( #concurrenthashmap )
11- - [ CopyOnWriteArrayList] ( #copyonwritearraylist )
12- - [ 资料] ( #资料 )
7+ - [ 一、同步容器] ( #一同步容器 )
8+ - [ 同步容器简介] ( #同步容器简介 )
9+ - [ 同步容器的问题] ( #同步容器的问题 )
10+ - [ 二、并发容器] ( #二并发容器 )
11+ - [ ConcurrentHashMap] ( #concurrenthashmap )
12+ - [ CopyOnWriteArrayList] ( #copyonwritearraylist )
13+ - [ 参考资料] ( #参考资料 )
1314
1415<!-- /TOC -->
1516
16- ## 同步容器
17+ ## 一、同步容器
18+
19+ ### 同步容器简介
1720
1821在 Java 中,同步容器主要包括 2 类:
1922
20- - Vector、Stack、HashTable
21- - Vector 实现了 List 接口,Vector 实际上就是一个数组,和 ArrayList 类似,但是 Vector 中的方法都是 synchronized 方法,即进行了同步措施。
22- - Stack 也是一个同步容器,它的方法也用 synchronized 进行了同步,它实际上是继承于 Vector 类。
23- - HashTable 实现了 Map 接口,它和 HashMap 很相似,但是 HashTable 进行了同步处理,而 HashMap 没有。
24- - Collections 类中提供的静态工厂方法创建的类(由 Collections.synchronizedXxxx 等方法)
23+ - ` Vector ` 、` Stack ` 、` Hashtable `
24+ - ` Vector ` - ` Vector ` 实现了 ` List ` 接口。` Vector ` 实际上就是一个数组,和 ` ArrayList ` 类似。但是 ` Vector ` 中的方法都是 ` synchronized ` 方法,即进行了同步措施。
25+ - ` Stack ` - ` Stack ` 也是一个同步容器,它的方法也用 ` synchronized ` 进行了同步,它实际上是继承于 ` Vector ` 类。
26+ - ` Hashtable ` - ` Hashtable ` 实现了 ` Map ` 接口,它和 ` HashMap ` 很相似,但是 ` Hashtable ` 进行了同步处理,而 ` HashMap ` 没有。
27+ - ` Collections ` 类中提供的静态工厂方法创建的类(由 ` Collections.synchronizedXxxx ` 等方法)
28+
29+ ### 同步容器的问题
2530
26- ### 同步容器的缺陷
31+ 同步容器的同步原理就是在方法上用 ` synchronized ` 修饰。 ** ` synchronized ` 可以保证在同一个时刻,只有一个线程可以执行某个方法或者某个代码块 ** 。
2732
28- 同步容器的同步原理就是在方法上用 ` synchronized ` 修饰。那么,这些方法每次只允许一个线程调用执行。
33+ > 想详细了解 ` synchronized ` 用法和原理可以参考: [ Java 并发核心机制之 synchronized ] ( https://github.com/dunwu/javacore/blob/master/docs/concurrent/java-concurrent-basic-mechanism.md#%E4%BA%8Csynchronized )
2934
3035#### 性能问题
3136
32- 由于被 ` synchronized ` 修饰的方法,每次只允许一个线程执行,其他试图访问这个方法的线程只能等待 。显然,这种方式比没有使用 ` synchronized ` 的容器性能要差。
37+ ` synchronized ` 的互斥同步会产生阻塞和唤醒线程的开销 。显然,这种方式比没有使用 ` synchronized ` 的容器性能要差。
3338
3439#### 安全问题
3540
36- 同步容器真的一定安全吗?
37-
38- 答案是:未必。同步容器未必真的安全。在做复合操作时,仍然需要加锁来保护。
41+ 同步容器真的绝对安全吗?
3942
40- 常见复合操作如下:
43+ 其实也未必。在做复合操作(非原子操作)时,仍然需要加锁来保护。 常见复合操作如下:
4144
42- - 迭代 :反复访问元素,直到遍历完全部元素;
43- - 跳转 :根据指定顺序寻找当前元素的下一个(下 n 个)元素;
44- - 条件运算:例如若没有则添加等;
45+ - ** 迭代 ** :反复访问元素,直到遍历完全部元素;
46+ - ** 跳转 ** :根据指定顺序寻找当前元素的下一个(下 n 个)元素;
47+ - ** 条件运算** :例如若没有则添加等;
4548
46- ##### 不安全的示例
49+ ❌ 不安全的示例
4750
4851``` java
49- public class Test {
50- static Vector<Integer > vector = new Vector<Integer > ();
51- public static void main (String [] args ) throws InterruptedException {
52- while (true ) {
53- for (int i= 0 ;i< 10 ;i++ )
52+ public class VectorDemo {
53+
54+ static Vector<Integer > vector = new Vector<> ();
55+
56+ public static void main (String [] args ) {
57+ while (true ) {
58+ vector. clear();
59+
60+ for (int i = 0 ; i < 10 ; i++ ) {
5461 vector. add(i);
55- Thread thread1 = new Thread (){
62+ }
63+
64+ Thread thread1 = new Thread () {
65+ @Override
5666 public void run () {
57- for (int i= 0 ;i < vector. size();i++ )
67+ for (int i = 0 ; i < vector. size(); i++ ) {
5868 vector. remove(i);
59- };
69+ }
70+ }
6071 };
61- Thread thread2 = new Thread (){
72+
73+ Thread thread2 = new Thread () {
74+ @Override
6275 public void run () {
63- for (int i= 0 ;i < vector. size();i++ )
76+ for (int i = 0 ; i < vector. size(); i++ ) {
6477 vector. get(i);
65- };
78+ }
79+ }
6680 };
81+
6782 thread1. start();
6883 thread2. start();
69- while (Thread . activeCount()> 10 ) {
7084
85+ while (Thread . activeCount() > 10 ) {
86+ System . out. println(" 同时存在 10 个以上线程,退出" );
87+ return ;
7188 }
7289 }
7390 }
91+
7492}
7593```
7694
77- 执行时可能会出现数组越界错误。
95+ 以上程序执行时可能会出现数组越界错误。
96+
97+ ` Vector ` 是线程安全的,那为什么还会报这个错?
7898
79- Vector 是线程安全的,为什么还会报这个错?很简单 ,对于 Vector,虽然能保证每一个时刻只能有一个线程访问它,但是不排除这种可能:
99+ 这是因为 ,对于 Vector,虽然能保证每一个时刻只能有一个线程访问它,但是不排除这种可能:
80100
81101当某个线程在某个时刻执行这句时:
82102
@@ -98,63 +118,78 @@ for(int i=0;i<vector.size();i++)
98118
99119那么通过 get 方法访问下标为 9 的元素肯定就会出问题了。
100120
101- ##### 安全示例
121+ ✔ 安全示例
102122
103123因此为了保证线程安全,必须在方法调用端做额外的同步措施,如下面所示:
104124
105125``` java
106- public class Test {
126+ public class VectorDemo2 {
127+
107128 static Vector<Integer > vector = new Vector<Integer > ();
108- public static void main (String [] args ) throws InterruptedException {
109- while (true ) {
110- for (int i= 0 ;i< 10 ;i++ )
129+
130+ public static void main (String [] args ) {
131+ while (true ) {
132+ for (int i = 0 ; i < 10 ; i++ ) {
111133 vector. add(i);
112- Thread thread1 = new Thread (){
134+ }
135+
136+ Thread thread1 = new Thread () {
137+ @Override
113138 public void run () {
114- synchronized (Test . class) { // 进行额外的同步
115- for (int i= 0 ;i < vector. size();i++ )
139+ synchronized (VectorDemo2 . class) { // 进行额外的同步
140+ for (int i = 0 ; i < vector. size(); i++ ) {
116141 vector. remove(i);
142+ }
117143 }
118- };
144+ }
119145 };
120- Thread thread2 = new Thread (){
146+
147+ Thread thread2 = new Thread () {
148+ @Override
121149 public void run () {
122- synchronized (Test . class) {
123- for (int i= 0 ;i < vector. size();i++ )
150+ synchronized (VectorDemo2 . class) {
151+ for (int i = 0 ; i < vector. size(); i++ ) {
124152 vector. get(i);
153+ }
125154 }
126- };
155+ }
127156 };
157+
128158 thread1. start();
129159 thread2. start();
130- while (Thread . activeCount()> 10 ) {
131160
161+ while (Thread . activeCount() > 10 ) {
162+ System . out. println(" 同时存在 10 个以上线程,退出" );
163+ return ;
132164 }
133165 }
134166 }
167+
135168}
136169```
137170
138- ##### ConcurrentModificationException 异常
171+ ` ConcurrentModificationException ` 异常
139172
140- 在对 Vector 等容器并发地进行迭代修改时,会报 ConcurrentModificationException 异常,关于这个异常将会在后续文章中讲述。
173+ 在对 Vector 等容器并发地进行迭代修改时,会报 ` ConcurrentModificationException ` 异常,关于这个异常将会在后续文章中讲述。
141174
142175但是在并发容器中不会出现这个问题。
143176
144- ## 并发容器
177+ ## 二、并发容器
178+
179+ 从前文可以知道,同步容器性能不高,也不能根本上保证线程安全,所以现代 Java 程序已经基本上将其弃用了。在并发场景下,取而代之的是并发容器。
145180
146- JDK 的 ` java.util.concurrent ` 包(即 juc)中提供了几个非常有用的并发容器 。
181+ J.U.C 包中提供了几个非常有用的并发容器 。
147182
148- - CopyOnWriteArrayList - 线程安全的 ArrayList
149- - CopyOnWriteArraySet - 线程安全的 Set,它内部包含了一个 CopyOnWriteArrayList,因此本质上是由 CopyOnWriteArrayList 实现的。
150- - ConcurrentSkipListSet - 相当于线程安全的 TreeSet。它是有序的 Set。它由 ConcurrentSkipListMap 实现。
151- - ConcurrentHashMap - 线程安全的 HashMap。采用分段锁实现高效并发。
152- - ConcurrentSkipListMap - 线程安全的有序 Map。使用跳表实现高效并发。
153- - ConcurrentLinkedQueue - 线程安全的无界队列。底层采用单链表。支持 FIFO。
154- - ConcurrentLinkedDeque - 线程安全的无界双端队列。底层采用双向链表。支持 FIFO 和 FILO。
155- - ArrayBlockingQueue - 数组实现的阻塞队列。
156- - LinkedBlockingQueue - 链表实现的阻塞队列。
157- - LinkedBlockingDeque - 双向链表实现的双端阻塞队列。
183+ - ` CopyOnWriteArrayList ` - 线程安全的 ` ArrayList ` 。
184+ - ` CopyOnWriteArraySet ` - 线程安全的 Set,它内部包含了一个 ` CopyOnWriteArrayList ` ,因此本质上是由 ` CopyOnWriteArrayList ` 实现的。
185+ - ` ConcurrentSkipListSet ` - 相当于线程安全的 ` TreeSet ` 。它是有序的 Set。它由 ` ConcurrentSkipListMap ` 实现。
186+ - ` ConcurrentHashMap ` - 线程安全的 ` HashMap ` 。采用分段锁实现高效并发。
187+ - ` ConcurrentSkipListMap ` - 线程安全的有序 Map。使用跳表实现高效并发。
188+ - ` ConcurrentLinkedQueue ` - 线程安全的无界队列。底层采用单链表。支持 FIFO。
189+ - ` ConcurrentLinkedDeque ` - 线程安全的无界双端队列。底层采用双向链表。支持 FIFO 和 FILO。
190+ - ` ArrayBlockingQueue ` - 数组实现的阻塞队列。
191+ - ` LinkedBlockingQueue ` - 链表实现的阻塞队列。
192+ - ` LinkedBlockingDeque ` - 双向链表实现的双端阻塞队列。
158193
159194### ConcurrentHashMap
160195
0 commit comments