欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java中的ReentrantReadWriteLock實(shí)現(xiàn)原理詳解

 更新時(shí)間:2024年01月13日 09:51:26   作者:java架構(gòu)師-太陽  
這篇文章主要介紹了Java中的ReentrantReadWriteLock實(shí)現(xiàn)原理詳解,讀寫鎖實(shí)現(xiàn)了接口ReadWriteLock,適合于讀多寫少的情況,支持公平鎖和非公平鎖,支持可沖入(進(jìn)入讀鎖后可再進(jìn)入讀鎖,進(jìn)入寫鎖后可再進(jìn)入寫鎖和讀鎖),需要的朋友可以參考下

介紹

讀寫鎖

  • 實(shí)現(xiàn)了接口ReadWriteLock
  • 適合于讀多寫少的情況
  • 支持公平鎖和非公平鎖
  • 支持可沖入(進(jìn)入讀鎖后可再進(jìn)入讀鎖,進(jìn)入寫鎖后可再進(jìn)入寫鎖和讀鎖)
  • 支持可沖入和公平與非公平

缺點(diǎn)

(1) 寫鎖饑餓問題,讀的線程很多,寫的線程搶占不到鎖,就一直搶占不到鎖,就饑餓

(2) 鎖降級(jí),獲取寫鎖后又再次獲取讀鎖(重入),釋放了寫鎖之后就變成了讀鎖,就是鎖降級(jí)

內(nèi)部接口

Sync/ReadLock/WriteLock/FairSync/NonfairSync 是其內(nèi)部類 下面圖有問題,ReadWriteLock沒有實(shí)現(xiàn)Lock

在這里插入圖片描述

代碼演示

public class Lock {   
   public static void main(String[] args) {
      new Thread(new Runnable() {
         @Override
         public void run() {
            for (int i = 0; i < 10; i++) {
               Lock.put(i + "", i + "");
            }
         }
      }).start();
      new Thread(new Runnable() {
         @Override
         public void run() {
            for (int i = 0; i < 10; i++) {
               Lock.get(i + "");
            }
         }
      }).start();
   }
   static Map<String, Object> map = new HashMap<String, Object>();
   static ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
   static java.util.concurrent.locks.Lock readLock = readWriteLock.readLock();
   static java.util.concurrent.locks.Lock writeLock = readWriteLock.writeLock();
   public static final Object get(String key) {
      readLock.lock();
      try {
         System.out.println("正在做讀的操作,key:" + key + "開始");
         Thread.sleep(100);
         Object object = map.get(key);
         System.out.println("正在做讀的操作,key:" + key + "結(jié)束");
         System.out.println();
         return object;
      } catch (InterruptedException e) {
         e.printStackTrace();
      } finally {
         readLock.unlock();
      }
      return key;
   }
   public static final Object put(String key, Object value) {
      writeLock.lock();
      try {
         System.out.println("正在做寫的操作,key:" + key + ",value:" + value + "開始");
         Thread.sleep(100);
         Object object = map.put(key, value);
         System.out.println("正在做寫的操作,key:" + key + ",value:" + value + "結(jié)束");
         System.out.println();
         return object;
      } catch (InterruptedException e) {
         e.printStackTrace();
      } finally {
         writeLock.unlock();
      }
      return value;
   }
   public static final void clear() {
      writeLock.lock();
      try {
         map.clear();
      } finally {
         writeLock.unlock();
      }
   }
}
class MyResource {

    Map<String,String> map = new HashMap<>();
    Lock lock = new ReentrantLock();
    ReadWriteLock rwLock = new ReentrantReadWriteLock();

    public void write(String key ,String value) {
        rwLock.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName()+"\t"+"正在寫入");
            map.put(key,value);
            //暫停毫秒
            try { TimeUnit.MILLISECONDS.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); }
            System.out.println(Thread.currentThread().getName()+"\t"+"完成寫入");
        }finally {
            rwLock.writeLock().unlock();
        }
    }

    public void read(String key) {
        rwLock.readLock().lock();
        try {
            System.out.println(Thread.currentThread().getName()+"\t"+"正在讀取");
            String result = map.get(key);

            // 暫停2000毫秒,演示讀鎖沒有完成之前,寫鎖無法獲得
            try { TimeUnit.MILLISECONDS.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); }
            System.out.println(Thread.currentThread().getName()+"\t"+"完成讀取"+"\t"+result);
        }finally {
            rwLock.readLock().unlock();
        }
    }


}


/**
 * @auther zzyy
 * @create 2022-04-08 18:18
 */
public class ReentrantReadWriteLockDemo {
    public static void main(String[] args) {
        MyResource myResource = new MyResource();

        for (int i = 1; i <=10; i++) {
            int finalI = i;
            new Thread(() -> {
                myResource.write(finalI +"", finalI +"");
            }, String.valueOf(i)).start();
        }

        for (int i = 1; i <=10; i++) {
            int finalI = i;
            new Thread(() -> {
                myResource.read(finalI +"");
            },String.valueOf(i)).start();
        }

        // 暫停幾秒鐘線程
        try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }

        // 在讀鎖沒有完成時(shí), 寫鎖不能寫入
        for (int i = 1; i <=3; i++) {
            int finalI = i;
            new Thread(() -> {
                myResource.write(finalI +"", finalI +"");
            },"新寫鎖線程->"+String.valueOf(i)).start();
        }
    }
}

實(shí)現(xiàn)原理

從表面來看,ReadLock和WriteLock是兩把鎖,實(shí)際上它只是同一把鎖的兩個(gè)視圖而已。

什么叫兩個(gè)視圖呢?可以理解為是一把鎖,線程分成兩類:讀線程和寫線程。讀線程和寫線程之間不互斥(可以同時(shí)拿到這把鎖),讀線程之間不互斥,寫線程之間互斥

從下面的構(gòu)造方法也可以看出,readerLock和writerLock實(shí)際共用同一個(gè)sync對(duì)象。

sync對(duì)象同互斥鎖一樣,分為非公平和公平兩種策略,并繼承自AQS

public ReentrantReadWriteLock() {
    this(false);           
}
public ReentrantReadWriteLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();    // fair為false使用非公平鎖,true使用公平鎖
    readerLock = new ReadLock(this);
    writerLock = new WriteLock(this);
}

同互斥鎖一樣,讀寫鎖也是用state變量來表示鎖狀態(tài)的。只是state變量在這里的含義和互斥鎖完全不同。在內(nèi)部類Sync中,對(duì)state變量進(jìn)行了重新定義,如下所示:

abstract static class Sync extends AbstractQueuedSynchronizer {
  // ...
  static final int SHARED_SHIFT = 16;
  static final int SHARED_UNIT = (1 << SHARED_SHIFT);
  static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1;
  static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
  // 持有讀鎖的線程的重入次數(shù)
  static int sharedCount(int c) { return c >>> SHARED_SHIFT; }
  // 持有寫鎖的線程的重入次數(shù)
  static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
  // ...
}

把 state變量拆成兩半,低16位用來記錄寫鎖。但同一時(shí)間既然只能有一個(gè)線程寫,為什么還需要16位呢?這是因?yàn)橐粋€(gè)寫線程可能多次重入。

例如,低16位的值等于5,表示一個(gè)寫線程重入了5次 高16位,用來讀鎖。

如高16位的值等于5,既可以表示5個(gè)讀線程都拿到了該鎖;也可以表示一個(gè)讀線程重入了5次

為什么要把一個(gè)int類型變量拆成兩半,而不是用兩個(gè)int型變量分別表示讀鎖和寫鎖的狀態(tài)呢?因?yàn)闊o法用一次CAS同時(shí)操作兩個(gè)int變量,所以用了一個(gè)int型的高16位和低16位分別表示讀鎖和寫鎖的狀態(tài)

當(dāng)state=0時(shí),說明既沒有線程持有讀鎖,也沒有線程持有寫鎖;當(dāng)state != 0時(shí),要么有線程持有讀鎖,要么有線程持有寫鎖,兩者不能同時(shí)成立,因?yàn)樽x和寫互斥。

這時(shí)再進(jìn)一步通過sharedCount(state)和exclusiveCount(state)判斷到底是讀線程還是寫線程持有了該鎖

到此這篇關(guān)于Java中的ReentrantReadWriteLock實(shí)現(xiàn)原理詳解的文章就介紹到這了,更多相關(guān)ReentrantReadWriteLock原理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論