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

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