CopyOnWriteArrayList和ReentrantReadWriteLock

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

综上对比,优先使用ReentrantReadWriteLock

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章