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();
}
}