Skip to content

Commit 80b7feb

Browse files
committed
Java
1 parent ed87c9b commit 80b7feb

1 file changed

Lines changed: 81 additions & 61 deletions

File tree

docs/Java_Concurrency/6_并发容器.md

Lines changed: 81 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,75 @@
11
# 并发容器
22

3+
## CopyOnWriteArrayList
4+
5+
### 1. 读写分离
6+
7+
**写操作在一个复制的数组**上进行,**读操作还是在原数组**中进行,**读写分离**,互不影响。
8+
9+
写操作需要加锁,防止并发写入时导致写入数据丢失。
10+
11+
写操作结束之后需要把原数组指向新的复制数组。
12+
13+
```java
14+
//写操作:
15+
//通过过创建底层数组的新副本来实现的。
16+
//当 List 需要被修改的时候,并不修改原有内容,而是对原有数据进行一次复制,将修改的内容写入副本。
17+
//写完之后,把原数组指向新的复制数组。
18+
//这样可以保证写操作实在一个复制的数组上进行,而读操作还是在原数组中进行,不会影响读操作。
19+
public boolean add(E e) {
20+
//加锁
21+
final ReentrantLock lock = this.lock;
22+
lock.lock();
23+
try {
24+
Object[] elements = getArray();
25+
int len = elements.length;
26+
// newElements 是一个复制的数组
27+
Object[] newElements = Arrays.copyOf(elements, len + 1);
28+
newElements[len] = e;
29+
// 写操作在一个复制的数组上进行
30+
setArray(newElements);
31+
return true;
32+
} finally {
33+
lock.unlock();
34+
}
35+
}
36+
37+
final void setArray(Object[] a) {
38+
array = a;
39+
}
40+
```
41+
42+
```java
43+
//读操作
44+
//读操作没有任何同步控制和锁操作,
45+
//因为内部数组 array 不会被修改。
46+
private transient volatile Object[] array;
47+
48+
public E get(int index) {
49+
return get(getArray(), index);
50+
}
51+
52+
@SuppressWarnings("unchecked")
53+
private E get(Object[] a, int index) {
54+
return (E) a[index];
55+
}
56+
57+
final Object[] getArray() {
58+
return array;
59+
}
60+
```
61+
62+
### 2. 适用场景
63+
64+
CopyOnWriteArrayList 在写操作的同时允许读操作,大大提高了读操作的性能,很适合**读多写少**的应用场景。
65+
66+
CopyOnWriteArrayList 有其缺陷:
67+
68+
- 内存占用:在写操作时需要复制一个新的数组,使得内存占用为原来的两倍左右;
69+
- 数据不一致:读操作不能读取实时性的数据,因为部分写操作的数据还未同步到读数组中。
70+
71+
所以 CopyOnWriteArrayList 不适合**内存敏感**以及对**实时性要求很高**的场景。
72+
373
## ConcurrentHashMap
474

575
### 1. 存储结构
@@ -162,72 +232,22 @@ static final <K,V> Node<K,V> tabAt(Node<K,V>[] tab, int i) {
162232

163233
5)其余情况把新的 Node 节点按链表或红黑树的方式插入到合适位置,这个过程采用内置锁实现并发。
164234

165-
## CopyOnWriteArrayList
166-
167-
### 1. 读写分离
168-
169-
**写操作在一个复制的数组**上进行,**读操作还是在原数组**中进行,**读写分离**,互不影响。
170-
171-
写操作需要加锁,防止并发写入时导致写入数据丢失。
172-
173-
写操作结束之后需要把原数组指向新的复制数组。
174-
175-
```java
176-
//写操作:
177-
//通过过创建底层数组的新副本来实现的。
178-
//当 List 需要被修改的时候,并不修改原有内容,而是对原有数据进行一次复制,将修改的内容写入副本。
179-
//写完之后,把原数组指向新的复制数组。
180-
//这样可以保证写操作实在一个复制的数组上进行,而读操作还是在原数组中进行,不会影响读操作。
181-
public boolean add(E e) {
182-
//加锁
183-
final ReentrantLock lock = this.lock;
184-
lock.lock();
185-
try {
186-
Object[] elements = getArray();
187-
int len = elements.length;
188-
// newElements 是一个复制的数组
189-
Object[] newElements = Arrays.copyOf(elements, len + 1);
190-
newElements[len] = e;
191-
// 写操作在一个复制的数组上进行
192-
setArray(newElements);
193-
return true;
194-
} finally {
195-
lock.unlock();
196-
}
197-
}
198-
199-
final void setArray(Object[] a) {
200-
array = a;
201-
}
202-
```
203-
204-
```java
205-
//读操作
206-
//读操作没有任何同步控制和锁操作,
207-
//因为内部数组 array 不会被修改。
208-
private transient volatile Object[] array;
235+
### 5.Hashtable 的区别
209236

210-
public E get(int index) {
211-
return get(getArray(), index);
212-
}
237+
**底层数据结构:**
213238

214-
@SuppressWarnings("unchecked")
215-
private E get(Object[] a, int index) {
216-
return (E) a[index];
217-
}
239+
- JDK1.7ConcurrentHashMap底层采用**分段的数组+链表**实现, JDK1.8ConcurrentHashMap底层采用的数据结构与JDK1.8HashMap的结构一样,**数组+链表/红黑二叉树**
240+
- HashtableJDK1.8 之前的HashMap的底层数据结构类似都是采用**数组+链表**的形式, 数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的。
218241

219-
final Object[] getArray() {
220-
return array;
221-
}
222-
```
242+
**实现线程安全的方式**
223243

224-
### 2. 适用场景
244+
- JDK1.7ConcurrentHashMap(分段锁)对整个桶数组进行了分割分段(Segment), 每一把锁只锁容器其中一部分数据,多线程访问容器里不同数据段的数据,就不会存在锁竞争,提高并发访问度。 JDK 1.8 采用**数组+链表/红黑二叉树**的数据结构来实现,并发控制使用**synchronizedCAS**来操作。
245+
- Hashtable:使用 synchronized 来保证线程安全,效率非常低下。 当一个线程访问同步方法时,其他线程也访问同步方法,可能会进入阻塞或轮询状态, 如使用 put 添加元素,另一个线程不能使用 put 添加元素,也不能使用 get,竞争会越来越激烈。
225246

226-
CopyOnWriteArrayList 在写操作的同时允许读操作,大大提高了读操作的性能,很适合**读多写少**的应用场景。
247+
Hashtable 全表锁
227248

228-
CopyOnWriteArrayList 有其缺陷:
249+
![img](https://gitee.com/duhouan/ImagePro/raw/master/java-notes/java/16_03.jpg)
229250

230-
- 内存占用:在写操作时需要复制一个新的数组,使得内存占用为原来的两倍左右;
231-
- 数据不一致:读操作不能读取实时性的数据,因为部分写操作的数据还未同步到读数组中。
251+
ConcurrentHashMap 分段锁
232252

233-
所以 CopyOnWriteArrayList 不适合**内存敏感**以及对**实时性要求很高**的场景。
253+
![img](https://gitee.com/duhouan/ImagePro/raw/master/java-notes/java/16_00.jpg)

0 commit comments

Comments
 (0)