CopyOnWriteArrayList適用於讀多寫少的併發場景,比如白名單,黑名單等場景。
在併發編程中,多個線程讀取一個數據集合是安全的;但是對於數據的修改操作比如add、remove等是不安全的。
CopyOnWriteArrayList底層實現就是讀取數據時不加鎖,修改數據時通過複製一份拷貝數據,在拷貝數據上進行修改來實現修改數據的安全性,在修改完成後,將指針指向新的數據集合。
缺點:
1、數據拷貝修改的過程,如果有讀取數據的過程,讀取到的數據是舊的數據集合;實時性比較查;
2、內存佔用,拷貝一份數據佔用內存空間可能會引起gc和full gc;
用法實例(實時性要求不高):
package com.company.com.copyonwritearraylist;
import java.util.List;
/**
* @author
* @date 2020/7/7
*/
public class ReadThread implements Runnable{
private List<Integer> list;
public ReadThread(List<Integer> list) {
this.list = list;
}
@Override
public void run() {
for(Integer num:list){
System.out.println(num
);
}
}
}
package com.company.com.copyonwritearraylist;
import java.util.List;
/**
* @author
* @date 2020/7/7
*/
public class WriteThread implements Runnable{
private List<Integer> list;
public WriteThread(List<Integer> list) {
this.list = list;
}
@Override
public void run() {
list.add(9);
}
}
package com.company.com.copyonwritearraylist;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import com.company.Main;
/**
* @author
* @date 2020/7/7
*/
public class TestCopyOnWriteArrayList {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1,2,3);
CopyOnWriteArrayList<Integer> copyOnWriteArrayList = new CopyOnWriteArrayList<>(list);
//ArrayList<Integer> copyOnWriteArrayList = new ArrayList<>(list);
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.execute(new ReadThread(copyOnWriteArrayList));
executorService.execute(new WriteThread(copyOnWriteArrayList));
executorService.execute(new WriteThread(copyOnWriteArrayList));
executorService.execute(new ReadThread(copyOnWriteArrayList));
executorService.execute(new WriteThread(copyOnWriteArrayList));
executorService.execute(new WriteThread(copyOnWriteArrayList));
executorService.execute(new ReadThread(copyOnWriteArrayList));
executorService.execute(new WriteThread(copyOnWriteArrayList));
executorService.execute(new WriteThread(copyOnWriteArrayList));
executorService.execute(new ReadThread(copyOnWriteArrayList));
executorService.execute(new WriteThread(copyOnWriteArrayList));
executorService.execute(new WriteThread(copyOnWriteArrayList));
executorService.execute(new ReadThread(copyOnWriteArrayList));
executorService.execute(new WriteThread(copyOnWriteArrayList));
executorService.execute(new WriteThread(copyOnWriteArrayList));
executorService.execute(new ReadThread(copyOnWriteArrayList));
executorService.execute(new WriteThread(copyOnWriteArrayList));
executorService.execute(new WriteThread(copyOnWriteArrayList));
executorService.execute(new ReadThread(copyOnWriteArrayList));
executorService.execute(new WriteThread(copyOnWriteArrayList));
executorService.execute(new WriteThread(copyOnWriteArrayList));
executorService.execute(new ReadThread(copyOnWriteArrayList));
executorService.execute(new WriteThread(copyOnWriteArrayList));
executorService.execute(new WriteThread(copyOnWriteArrayList));
}
}
ReentrantReadWriteLock 可重入讀寫鎖
有兩把鎖,一個讀鎖,一個寫鎖;
獲取讀鎖:沒有其他線程佔用寫鎖,本線程擁有寫鎖,可以獲取讀鎖;
獲取寫鎖:沒有其他線程佔用讀鎖,也沒有其他線程佔用寫鎖;
包含一個 ReadLock 和 一個 WriteLock 對象
讀鎖與讀鎖不互斥;讀鎖與寫鎖,寫鎖與寫鎖互斥
適合對共享資源有讀和寫操作,寫操作很少,讀操作頻繁的場景
可以從寫鎖降級到讀鎖。獲取寫鎖->獲取讀鎖->釋放寫鎖
無法從讀鎖升級到寫鎖
讀寫鎖支持中斷
寫鎖支持Condition;讀鎖不支持Condition
ReadWriteLock rtLock = new ReentrantReadWriteLock();
rtLock.writeLock().lock();
System.out.println(“writeLock”);
rtLock.readLock().lock();
System.out.println(“readLock”);
上面這段代碼屬於鎖降級,不會導致死鎖,但沒有正確的釋放鎖,從寫鎖降級成讀鎖不會自動釋放當前線程獲取的寫鎖,仍然需要主動的釋放,否則別的線程永遠也獲取不到寫鎖。
ReadWriteLock rtLock = new ReentrantReadWriteLock();
rtLock.readLock().lock();
System.out.println(“readLock”);
rtLock.writeLock().lock();
System.out.println(“writeLock”);
上面這段代碼屬於鎖升級,會產生死鎖,因爲同一個線程在沒有釋放讀鎖的情況下就去申請寫鎖是不成立的,讀寫和寫寫是互斥的。
ReentrantReadWriteLock用法示例
package com.company.com.writereadlock;
import java.util.HashMap;
import java.util.Random;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* @author
* @date 2020/7/7
*/
public class TestWtriteReadLock {
HashMap<String,Integer> map = new HashMap<>();
ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
public static void main(String[] args) {
TestWtriteReadLock test = new TestWtriteReadLock();
Double d = Math.random()*1000;
for(int i =0; i<5;i++){
String key = 1+"";
new Thread(){
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(test.getValue(key));
}
}.start();
}
for(int i =0; i<5;i++){
String key = i+"";
Integer num = Integer.valueOf(i);
int finalI = i;
new Thread(){
@Override
public void run() {
test.set(key,num);
}
}.start();
}
}
public Integer getValue(String key){
readWriteLock.readLock().lock();
Integer value = map.get(key);
readWriteLock.readLock().unlock();
return value;
}
public void set(String key,Integer value){
readWriteLock.writeLock().lock();
map.put(key,value);
readWriteLock.writeLock().unlock();
}
}