亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 開發 > Java > 正文

Java編程讀寫鎖詳解

2024-07-14 08:41:55
字體:
來源:轉載
供稿:網友

ReadWriteLock也是一個接口,提供了readLock和writeLock兩種鎖的操作機制,一個資源可以被多個線程同時讀,或者被一個線程寫,但是不能同時存在讀和寫線程。 

基本規則: 讀讀不互斥 讀寫互斥 寫寫互斥

問題: 既然讀讀不互斥,為何還要加讀鎖

答: 如果只是讀,是不需要加鎖的,加鎖本身就有性能上的損耗

如果讀可以不是最新數據,也不需要加鎖

如果讀必須是最新數據,必須加讀寫鎖

讀寫鎖相較于互斥鎖的優點僅僅是允許讀讀的并發,除此之外并無其他。

結論: 讀寫鎖能夠保證讀取數據的 嚴格實時性,如果不需要這種 嚴格實時性,那么不需要加讀寫鎖。

簡單實現:

package readandwrite; import java.util.Random;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.locks.ReentrantReadWriteLock; public class MyTest { private static ReentrantReadWriteLock rwl=new ReentrantReadWriteLock(); private static double data=0; static class readClass implements Runnable{  @Override  public void run() {   rwl.readLock().lock();   System.out.println("讀數據:"+data);   rwl.readLock().unlock();  } }  static class writeClass implements Runnable{  private double i;    public writeClass(double i) {   this.i = i;  }   @Override  public void run() {   rwl.writeLock().lock();   data=i;   System.out.println("寫數據: "+data);   rwl.writeLock().unlock();  }   }   public static void main(String[] args) throws InterruptedException {  ExecutorService pool=Executors.newCachedThreadPool();  for(int i=0;i<10;i++){   pool.submit(new readClass());   pool.submit(new writeClass((double)new Random().nextDouble()));   pool.submit(new writeClass((double)new Random().nextDouble()));   Thread.sleep(1000);  }     pool.shutdown(); }  }

之前我們提到的鎖都是排它鎖(同一時刻只允許一個線程進行訪問),而讀寫鎖維護了一對鎖,一個讀鎖,一個寫鎖。讀寫鎖在同一時刻允許多個線程進行讀操作,但是寫線程訪問過程中,所有的讀線程和其他寫線程均被阻塞。如此,并發性有了很大的提升。這樣,在某些讀遠遠大于寫的場景中,讀寫鎖能夠提供比排它鎖更好的并發量和吞吐量。

一個關于讀寫鎖的Demo:

分析:設計一個模擬隊列,擁有一個data成員變量用于存儲數據和存取兩種操作。

import java.util.Random;import java.util.concurrent.locks.ReadWriteLock;import java.util.concurrent.locks.ReentrantReadWriteLock;public class ReadWriteLockDemo{  public static void main(String[] args)  {    DefQueue queue = new DefQueue();    for (int i = 1; i < 10; i++)    {      //啟動線程進行讀操作      new Thread(new Runnable()      {        @Override        public void run()        {          while (true)          {            queue.get();          }        }      }).start();      //啟動線程進行寫操作      new Thread(new Runnable()      {        @Override        public void run()        {          while(true)          {            queue.put(new Random().nextInt(10000));          }        }      }).start();    }  }}class DefQueue{  private int data;  ReadWriteLock rwLock = new ReentrantReadWriteLock();  public void get()  {    rwLock.readLock().lock();//加讀鎖    try    {      System.out.println(Thread.currentThread().getName() + "be ready to get data");      Thread.sleep((long) (Math.random() * 1000));      System.out.println(Thread.currentThread().getName() + "get the data:  " + data);    } catch (InterruptedException e)    {      e.printStackTrace();    } finally    {      rwLock.readLock().unlock();//釋放讀鎖    }  }  public void put(int data)  {    rwLock.writeLock().lock();//加寫鎖    try    {      System.out.println(Thread.currentThread().getName() + " be ready to write data");      Thread.sleep((long) (Math.random() * 1000));      this.data = data;      System.out.println(Thread.currentThread().getName() + " has wrote the data: "+data);    } catch (InterruptedException e)    {      e.printStackTrace();    } finally    {      rwLock.writeLock().unlock();//釋放寫鎖    }  }}

程序部分運行結果:

Thread-0be ready to get dataThread-0get the data:  0Thread-1 be ready to write dataThread-1 has wrote the data: 1156Thread-2be ready to get dataThread-2get the data:  1156Thread-3 be ready to write dataThread-3 has wrote the data: 9784Thread-3 be ready to write dataThread-3 has wrote the data: 4370Thread-3 be ready to write dataThread-3 has wrote the data: 1533Thread-4be ready to get dataThread-4get the data:  1533Thread-5 be ready to write dataThread-5 has wrote the data: 2345Thread-6be ready to get dataThread-6get the data:  2345Thread-9 be ready to write dataThread-9 has wrote the data: 9463Thread-9 be ready to write dataThread-9 has wrote the data: 9301Thread-9 be ready to write dataThread-9 has wrote the data: 549Thread-9 be ready to write dataThread-9 has wrote the data: 4673Thread-9 be ready to write data

我們可以看到打印語句結果很正常。

下面我們再來實現一個模擬緩沖區的小Demo:

import java.util.HashMap;import java.util.Map;import java.util.concurrent.locks.ReadWriteLock;import java.util.concurrent.locks.ReentrantReadWriteLock;/* * @author vayne *  * 多線程實現緩存的小demo */class Cachend{  volatile Map<String, String> cachmap = new HashMap<String, String>();//加volatile關鍵字保證可見性。  ReadWriteLock rwLock = new ReentrantReadWriteLock();//這個讀寫鎖要定義在方法外面,使得每一個線程用的是同一個讀寫鎖。  public String getS(String key)           //如果定義在方法內部,就是跟方法棧有關的讀寫鎖。這樣可能不是同一個鎖。  {    rwLock.readLock().lock();    String value = null;    try    {      value = cachmap.get(key);      if (cachmap.get(key) == null)//這里要重新獲得key對應的value值      {        rwLock.readLock().unlock();        rwLock.writeLock().lock();        try        {          if (cachmap.get(key) == null)//這里也是          {            value = "" + Thread.currentThread().getName();            cachmap.put(key, value);            System.out.println(Thread.currentThread().getName() + " put the value ::::" + value);          }        } finally        {          rwLock.readLock().lock();  //將鎖降級,這里跟下一句的順序不能反。          rwLock.writeLock().unlock();//關于這里的順序問題,下面我會提到。        }      }    } finally    {      rwLock.readLock().unlock();    }    return cachmap.get(key);  }}public class CachendDemo{  public static void main(String[] args)  {    Cachend ca = new Cachend();    for (int i = 0; i < 4; i++)    {      new Thread(new Runnable()      {        @Override        public void run()        {          System.out.println(Thread.currentThread().getName()+" "+ca.getS("demo1"));          System.out.println(Thread.currentThread().getName()+" "+ca.cachmap.entrySet());        }      }).start();    }  }}

運行結果:

Thread-0 put the value ::::Thread-0Thread-0 Thread-0Thread-0 [demo1=Thread-0]Thread-2 Thread-0Thread-2 [demo1=Thread-0]Thread-3 Thread-0Thread-3 [demo1=Thread-0]Thread-1 Thread-0Thread-1 [demo1=Thread-0]

上面我給出了一些注釋,其實這個代碼是很不好寫的,考慮的東西很多。下面我來講一下上面的代碼中提到的順序問題。

對于讀寫鎖我們應該了解下面的一些性質(這些性質是由源代碼得出來的,因為源代碼的設計,所以才有下列性質):

  • 如果存在讀鎖,則寫鎖不能被獲取,原因在于:讀寫鎖要確保寫鎖的操作對讀鎖可見。,如果允許讀鎖在已被獲取的情況下對寫鎖的獲取,那么正在運行的其他讀線程就無法感知到當前寫線程的操作。因此,只有等待其他讀線程都釋放了讀鎖,寫鎖才能被當前線程獲取,而寫鎖一旦被獲取,則其他讀寫線程的后續訪問將會被阻塞。
  • 鎖降級:指的是寫鎖降級成為讀鎖。具體操作是獲取到寫鎖之后,在釋放寫鎖之前,要先再次獲取讀鎖。這也就是上面我寫注釋提醒大家注意的地方。為什么要這樣處理呢,答案就是為了保證數據可見性。如果當前線程不獲取讀鎖而是直接釋放寫鎖,假設此刻另一個線程(記作T)獲取了寫鎖并修改了數據,那么當前線程無法感知線程T的數據更新。如果當前線程獲取讀鎖,即遵循鎖降級的步驟,則線程T將會被阻塞,知道當前線程使用數據并釋放讀鎖之后,T才能獲取寫鎖進行數據更新。

第二條對應我們上面的程序就是,如果我們添加了“demo1”對應的value值,然后釋放了寫鎖,此時在當前線程S還未獲得讀鎖時,另一個線程T又獲得了寫鎖,那么就會將S的操作給覆蓋(如果取到的值已經緩存在S中,那么T的操作就無法被S感知了,到最后依然會返回S操作的值)。

再來看一個DEMO:

讀寫鎖,分為讀鎖和寫鎖,多個讀鎖不互斥,讀鎖和寫鎖互斥,寫鎖與寫鎖互斥,這是JVM自己控制的,你只要上好相應的鎖即可,如果你的代碼只讀數據,可以很多人同時讀,但不能同時寫,那就上讀鎖;如果你的代碼修改數據,只能有一個人在寫,且不能同時讀取,那就上寫鎖.總之,讀的時候上讀鎖,寫的時候上寫鎖!

看如下程序: 新建6個線程,3個線程用來讀,3個線程用來寫,

package javaplay.thread.test; import java.util.Random;import java.util.concurrent.locks.ReadWriteLock;import java.util.concurrent.locks.ReentrantReadWriteLock; public class ReadWriteLockTest {  public static void main(String[] args) {    final Queue3 q3 = new Queue3();    for (int i = 0; i < 3; i++) {      new Thread() {        public void run() {          while (true) {            q3.get();          }        }      }.start();      new Thread() {        public void run() {          while (true) {            q3.put(new Random().nextInt(10000));          }        }      }.start();    }  }} class Queue3 {  private Object data = null;// 共享數據,只能有一個線程能寫該數據,但可以有多個線程同時讀該數據。  // 讀寫鎖  ReadWriteLock rwl = new ReentrantReadWriteLock();   // 相當于讀操作  public void get() {    rwl.readLock().lock();    try {      System.out.println(Thread.currentThread().getName() + " be ready to read data!");      Thread.sleep((long) (Math.random() * 1000));      System.out.println(Thread.currentThread().getName() + "have read data :" + data);    } catch (InterruptedException e) {      e.printStackTrace();    } finally {      rwl.readLock().unlock();    }  }   // 相當于寫操作  public void put(Object data) {    rwl.writeLock().lock();    try {      System.out.println(Thread.currentThread().getName() + " be ready to write data!");      Thread.sleep((long) (Math.random() * 1000));      this.data = data;      System.out.println(Thread.currentThread().getName() + " have write data: " + data);    } catch (InterruptedException e) {      e.printStackTrace();    } finally {      rwl.writeLock().unlock();    }  }}

讀寫鎖功能很強大!這樣可以實現正常的邏輯,如果我們把讀寫鎖相關的代碼注釋,發現程序正準備寫的時候,就有線程讀了,發現準備讀的時候,有線程去寫,這樣不符合我們的邏輯;通過Java5的新特新可以很輕松的解決這樣的問題;

查看Java API ReentrantReadWriteLock 上面有經典(緩存)的用法,下面是doc里面的偽代碼,,它演示的是一個實體的緩存,不是緩存系統,相當于緩存代理,注意volatile的運用:

package javaplay.thread.test; import java.util.concurrent.locks.ReentrantReadWriteLock; /* * Sample usages. Here is a code sketch showing how to perform lock downgrading after updating a cache  * (exception handling is particularly tricky when handling multiple locks in a non-nested fashion): */class CachedData {  Object data;  volatile boolean cacheValid;  final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();   void processCachedData() {    rwl.readLock().lock();    if (!cacheValid) {      // Must release read lock before acquiring write lock      rwl.readLock().unlock();      rwl.writeLock().lock();      try {        // Recheck state because another thread might have        // acquired write lock and changed state before we did.        if (!cacheValid) {          data = ...          cacheValid = true;        }        // Downgrade by acquiring read lock before releasing write lock        rwl.readLock().lock();      } finally {        rwl.writeLock().unlock(); // Unlock write, still hold read      }    }     try {      use(data);    } finally {      rwl.readLock().unlock();    }  }}

假設現在多個線程來讀了,那第一個線程讀到的數據是空的,那它就要寫就要填充數據,那么第二個第三個就應該互斥等著,一進來是來讀數據的所以上讀鎖,進來后發現數據是空的,就先把讀鎖釋放再重新獲取寫鎖,就開始寫數據,數據寫完了,就把寫鎖釋放,把讀鎖重新掛上,持有讀鎖時不能同時獲取寫鎖,但擁有寫鎖時可同時再獲取讀鎖,自己線程掛的寫鎖可同時掛讀鎖的,這就是降級,就是除了讀鎖和寫鎖外,還有讀寫鎖也叫更新鎖,就是自己即可以讀又可以寫的鎖,也就是在自己擁有寫鎖還沒釋放寫鎖時就獲取了讀鎖就降級為讀寫鎖/更新鎖,但是不能在持有讀鎖時再獲取寫鎖;

基于上面的例子,我們可以實現一個緩存系統:

package javaplay.thread.test; import java.util.HashMap;import java.util.Map;import java.util.concurrent.locks.ReadWriteLock;import java.util.concurrent.locks.ReentrantReadWriteLock; public class CacheDemo {  private Map<String, Object> cache = new HashMap<>();   public static void main(String[] args) {   }   // 可做到多個線程并必的讀 讀和寫又互斥 系統性能很高  // 這就是讀寫鎖的價值  private ReadWriteLock rwl = new ReentrantReadWriteLock();   public Object getData(String key) {    rwl.readLock().lock();    Object value = null;    try {      value = cache.get(key);      if (value == null) {// 避免首次多次查詢要加synchronized        rwl.readLock().unlock();        rwl.writeLock().lock();        try {          if (value == null) // 就算第二個第三個線程進來時也不用再寫了 跟偽代碼相同原理            value = "aaa";// 實際去query db        } finally {          rwl.writeLock().unlock();        }        rwl.readLock().lock();      }    } finally {      rwl.readLock().unlock();    }    return value;  }}錯誤之處:沒有把不存在的值put;要用get(key)來判空

感謝大家對VeVb武林網的支持。


注:相關教程知識閱讀請移步到JAVA教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美放荡办公室videos4k| 国产在线a不卡| 日韩精品免费在线视频观看| 欧美性xxxxxx| 欧美日韩在线视频一区二区| 亚洲xxxx视频| 国产丝袜高跟一区| 最近2019免费中文字幕视频三| 欧美华人在线视频| 久久久国产精品视频| 国内精品久久久久影院优| 中文字幕亚洲欧美在线| 国产69精品久久久久久| 亚洲国产精品视频在线观看| 久久久成人av| 国产成人在线一区二区| 欧美专区国产专区| 国产精品福利在线观看| 日韩欧美在线看| 欧美一区二区大胆人体摄影专业网站| 欧美视频裸体精品| 国产成人a亚洲精品| 欧美一级淫片videoshd| 国产精国产精品| 亚洲日韩欧美视频| 国产在线视频91| 日韩av日韩在线观看| 国产成人精品最新| 欧美成人精品在线播放| 日本久久久久久久久| 国产精品日韩一区| 97超碰国产精品女人人人爽| 色噜噜久久综合伊人一本| 日韩欧美主播在线| 亚洲少妇激情视频| 欧美亚洲一区在线| 欧美性xxxxxxx| 亚洲三级免费看| 成人性教育视频在线观看| 26uuu另类亚洲欧美日本一| 91精品久久久久久久久久久久久久| 97香蕉久久夜色精品国产| 中文字幕av一区中文字幕天堂| 2020国产精品视频| 日韩视频免费在线| 欧美日韩性视频| 日本久久久a级免费| 最近2019年好看中文字幕视频| 成人淫片在线看| 日韩美女在线播放| 91精品91久久久久久| 亚洲激情视频在线| 色樱桃影院亚洲精品影院| 国产精品一区=区| 三级精品视频久久久久| 国产精品国产亚洲伊人久久| 一本大道久久加勒比香蕉| 91九色单男在线观看| 88xx成人精品| 国产精品91久久| 色偷偷av一区二区三区| 欧美高清性猛交| 成人国产精品久久久久久亚洲| 日韩电视剧在线观看免费网站| 成人国产精品免费视频| 97超视频免费观看| 亚洲天天在线日亚洲洲精| 欧美日韩国产中字| 91高清视频免费| 91国产美女在线观看| 高清欧美性猛交| 视频在线观看一区二区| 亚洲精品99久久久久中文字幕| 亚洲韩国青草视频| 亚洲成人黄色在线观看| 国产成人黄色av| 日韩av在线免费观看一区| 欧美www视频在线观看| 美女久久久久久久久久久| 国产成人免费91av在线| 国产精品久久久精品| 亚洲最大福利视频网| 日韩风俗一区 二区| 国产午夜精品久久久| 欧美成人激情视频| 久久久久久美女| 在线观看久久av| 亚洲香蕉成视频在线观看| 久久久久亚洲精品成人网小说| 国产成人在线亚洲欧美| 欧美国产在线视频| 精品久久久香蕉免费精品视频| 国产精品流白浆视频| 日韩电视剧在线观看免费网站| 亚洲品质视频自拍网| 亚洲视频第一页| 在线播放国产一区二区三区| 尤物yw午夜国产精品视频明星| 国产精品成人aaaaa网站| 欧美极品少妇xxxxⅹ免费视频| 亚洲国产97在线精品一区| 青草青草久热精品视频在线观看| 在线观看免费高清视频97| 久久全球大尺度高清视频| 成人免费福利视频| 亚洲精品网址在线观看| 欧美日韩成人黄色| 亚洲在线免费观看| 欧美成人免费在线观看| 日韩在线视频观看正片免费网站| 日韩电影免费在线观看中文字幕| 欧美中文在线观看国产| 亚洲国产精品资源| 国产精品白嫩美女在线观看| 国产精品欧美日韩一区二区| 在线观看日韩www视频免费| 欧美国产精品日韩| 欧美激情一区二区三区久久久| 欧美成人性生活| 精品中文字幕在线观看| 国产精品自产拍在线观看中文| 久久久久日韩精品久久久男男| 欧美人成在线视频| 欧美超级乱淫片喷水| 日韩电视剧免费观看网站| 97在线免费观看视频| 欧美激情三级免费| 亚洲精品成人久久久| 97av在线影院| 久久精品亚洲精品| 中文字幕免费精品一区高清| 亚洲在线视频福利| 有码中文亚洲精品| 成人激情在线播放| 国产精品美女www爽爽爽视频| 日韩成人在线网站| 欧美孕妇毛茸茸xxxx| 亚洲香蕉伊综合在人在线视看| 自拍偷拍亚洲区| 久久久久成人网| 国产偷亚洲偷欧美偷精品| 福利微拍一区二区| 尤物yw午夜国产精品视频| 国产精品成av人在线视午夜片| 日韩在线观看你懂的| 国产精品久久久久久久久久久久久| 亚洲第一色中文字幕| 啪一啪鲁一鲁2019在线视频| 国产成人免费av电影| 国产丝袜一区二区三区免费视频| 91av在线免费观看| 欧美主播福利视频| 久久久久久久久久久免费| 欧美一级片在线播放| 亚洲成人激情在线观看| 久久精品成人一区二区三区| 国产色视频一区| 色偷偷偷综合中文字幕;dd| 成人字幕网zmw| 米奇精品一区二区三区在线观看| 最近免费中文字幕视频2019| 久久亚洲精品视频| 91精品久久久久久久久久另类| 欧洲成人免费视频|