最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java編程讀寫鎖詳解

 更新時(shí)間:2018年08月06日 09:02:52   作者:屌絲碼農(nóng)  
本篇文章給大家詳細(xì)分享了Java編程讀寫鎖的相關(guān)原理以及知識(shí)點(diǎn)內(nèi)容,有興趣的朋友們可以參考下。

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

基本規(guī)則: 讀讀不互斥 讀寫互斥 寫寫互斥

問(wèn)題: 既然讀讀不互斥,為何還要加讀鎖

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

如果讀可以不是最新數(shù)據(jù),也不需要加鎖

如果讀必須是最新數(shù)據(jù),必須加讀寫鎖

讀寫鎖相較于互斥鎖的優(yōu)點(diǎn)僅僅是允許讀讀的并發(fā),除此之外并無(wú)其他。

結(jié)論: 讀寫鎖能夠保證讀取數(shù)據(jù)的 嚴(yán)格實(shí)時(shí)性,如果不需要這種 嚴(yán)格實(shí)時(shí)性,那么不需要加讀寫鎖。

簡(jiǎn)單實(shí)現(xiàn):

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("讀數(shù)據(jù):"+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("寫數(shù)據(jù): "+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();
 }
 
 
}

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

一個(gè)關(guān)于讀寫鎖的Demo:

分析:設(shè)計(jì)一個(gè)模擬隊(duì)列,擁有一個(gè)data成員變量用于存儲(chǔ)數(shù)據(jù)和存取兩種操作。

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++)
    {
      //啟動(dòng)線程進(jìn)行讀操作
      new Thread(new Runnable()
      {
        @Override
        public void run()
        {
          while (true)
          {
            queue.get();
          }
        }

      }).start();

      //啟動(dòng)線程進(jìn)行寫操作
      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();//釋放寫鎖
    }

  }
}

程序部分運(yùn)行結(jié)果:

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

我們可以看到打印語(yǔ)句結(jié)果很正常。

下面我們?cè)賮?lái)實(shí)現(xiàn)一個(gè)模擬緩沖區(qū)的小Demo:

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/*
 * @author vayne
 * 
 * 多線程實(shí)現(xiàn)緩存的小demo
 */
class Cachend
{
  volatile Map<String, String> cachmap = new HashMap<String, String>();//加volatile關(guān)鍵字保證可見(jiàn)性。

  ReadWriteLock rwLock = new ReentrantReadWriteLock();//這個(gè)讀寫鎖要定義在方法外面,使得每一個(gè)線程用的是同一個(gè)讀寫鎖。
  public String getS(String key)           //如果定義在方法內(nèi)部,就是跟方法棧有關(guān)的讀寫鎖。這樣可能不是同一個(gè)鎖。
  {
    rwLock.readLock().lock();
    String value = null;
    try
    {
      value = cachmap.get(key);

      if (cachmap.get(key) == null)//這里要重新獲得key對(duì)應(yīng)的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();  //將鎖降級(jí),這里跟下一句的順序不能反。
          rwLock.writeLock().unlock();//關(guān)于這里的順序問(wèn)題,下面我會(huì)提到。
        }
      }

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

運(yùn)行結(jié)果:

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

上面我給出了一些注釋,其實(shí)這個(gè)代碼是很不好寫的,考慮的東西很多。下面我來(lái)講一下上面的代碼中提到的順序問(wèn)題。

對(duì)于讀寫鎖我們應(yīng)該了解下面的一些性質(zhì)(這些性質(zhì)是由源代碼得出來(lái)的,因?yàn)樵创a的設(shè)計(jì),所以才有下列性質(zhì)):

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

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

再來(lái)看一個(gè)DEMO:

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

看如下程序: 新建6個(gè)線程,3個(gè)線程用來(lái)讀,3個(gè)線程用來(lái)寫,

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;// 共享數(shù)據(jù),只能有一個(gè)線程能寫該數(shù)據(jù),但可以有多個(gè)線程同時(shí)讀該數(shù)據(jù)。
  // 讀寫鎖
  ReadWriteLock rwl = new ReentrantReadWriteLock();
 
  // 相當(dāng)于讀操作
  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();
    }
  }
 
  // 相當(dāng)于寫操作
  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();
    }
  }
}

讀寫鎖功能很強(qiáng)大!這樣可以實(shí)現(xiàn)正常的邏輯,如果我們把讀寫鎖相關(guān)的代碼注釋,發(fā)現(xiàn)程序正準(zhǔn)備寫的時(shí)候,就有線程讀了,發(fā)現(xiàn)準(zhǔn)備讀的時(shí)候,有線程去寫,這樣不符合我們的邏輯;通過(guò)Java5的新特新可以很輕松的解決這樣的問(wèn)題;

查看Java API ReentrantReadWriteLock 上面有經(jīng)典(緩存)的用法,下面是doc里面的偽代碼,,它演示的是一個(gè)實(shí)體的緩存,不是緩存系統(tǒng),相當(dāng)于緩存代理,注意volatile的運(yùn)用:

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

假設(shè)現(xiàn)在多個(gè)線程來(lái)讀了,那第一個(gè)線程讀到的數(shù)據(jù)是空的,那它就要寫就要填充數(shù)據(jù),那么第二個(gè)第三個(gè)就應(yīng)該互斥等著,一進(jìn)來(lái)是來(lái)讀數(shù)據(jù)的所以上讀鎖,進(jìn)來(lái)后發(fā)現(xiàn)數(shù)據(jù)是空的,就先把讀鎖釋放再重新獲取寫鎖,就開(kāi)始寫數(shù)據(jù),數(shù)據(jù)寫完了,就把寫鎖釋放,把讀鎖重新掛上,持有讀鎖時(shí)不能同時(shí)獲取寫鎖,但擁有寫鎖時(shí)可同時(shí)再獲取讀鎖,自己線程掛的寫鎖可同時(shí)掛讀鎖的,這就是降級(jí),就是除了讀鎖和寫鎖外,還有讀寫鎖也叫更新鎖,就是自己即可以讀又可以寫的鎖,也就是在自己擁有寫鎖還沒(méi)釋放寫鎖時(shí)就獲取了讀鎖就降級(jí)為讀寫鎖/更新鎖,但是不能在持有讀鎖時(shí)再獲取寫鎖;

基于上面的例子,我們可以實(shí)現(xiàn)一個(gè)緩存系統(tǒng):

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) {
 
  }
 
  // 可做到多個(gè)線程并必的讀 讀和寫又互斥 系統(tǒng)性能很高
  // 這就是讀寫鎖的價(jià)值
  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) // 就算第二個(gè)第三個(gè)線程進(jìn)來(lái)時(shí)也不用再寫了 跟偽代碼相同原理
            value = "aaa";// 實(shí)際去query db
        } finally {
          rwl.writeLock().unlock();
        }
        rwl.readLock().lock();
      }
    } finally {
      rwl.readLock().unlock();
    }
    return value;
  }
}
錯(cuò)誤之處:沒(méi)有把不存在的值put;要用get(key)來(lái)判空

感謝大家對(duì)腳本之家的支持。

相關(guān)文章

  • 一文詳解Java二分查找算法

    一文詳解Java二分查找算法

    二分查找(binary search),也稱折半搜索,是一種在有序數(shù)組中查找某一特定元素的搜索算法,接下來(lái)就來(lái)給大家講講都有哪些查找算法,以及經(jīng)典的二分查找法該如何實(shí)現(xiàn),需要的朋友可以參考下
    2023-07-07
  • Java中如何模擬HTTP請(qǐng)求并驗(yàn)證功能

    Java中如何模擬HTTP請(qǐng)求并驗(yàn)證功能

    要模擬HTTP請(qǐng)求并驗(yàn)證功能,你可以使用Spring Boot提供的MockMvc工具,它允許我們?cè)跊](méi)有實(shí)際啟動(dòng)HTTP服務(wù)器的情況下測(cè)試Spring MVC控制器,下面給大家分享如何模擬HTTP請(qǐng)求并驗(yàn)證功能,感興趣的朋友一起看看吧
    2024-05-05
  • 利用Java對(duì)比兩個(gè)PDF文件之間的差異

    利用Java對(duì)比兩個(gè)PDF文件之間的差異

    這篇文章主要為大家詳細(xì)介紹了如何在 Java 程序中通過(guò)代碼快速比較兩個(gè) PDF 文檔并找出文檔之間的內(nèi)容差異,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2023-10-10
  • Java for循環(huán)性能優(yōu)化實(shí)現(xiàn)解析

    Java for循環(huán)性能優(yōu)化實(shí)現(xiàn)解析

    這篇文章主要介紹了Java for循環(huán)性能優(yōu)化實(shí)現(xiàn)解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-01-01
  • 淺談JDK7和JDK8的區(qū)別在哪

    淺談JDK7和JDK8的區(qū)別在哪

    面試總是遇到這個(gè)問(wèn)題,做一個(gè)小總結(jié),文中有非常詳細(xì)的介紹,對(duì)正在學(xué)習(xí)java的小伙伴們很有幫助,需要的朋友可以參考下
    2021-06-06
  • Spring Boot如何獲取maven打包時(shí)間

    Spring Boot如何獲取maven打包時(shí)間

    這篇文章主要介紹了Spring Boot如何獲取maven打包時(shí)間,首先引入maven打包插件,本文分步驟給大家介紹的非常詳細(xì),需要的朋友參考下吧
    2024-03-03
  • SpringBoot之QueryDsl嵌套子查詢問(wèn)題

    SpringBoot之QueryDsl嵌套子查詢問(wèn)題

    這篇文章主要介紹了SpringBoot之QueryDsl嵌套子查詢問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • Java并發(fā)編程-volatile可見(jiàn)性詳解

    Java并發(fā)編程-volatile可見(jiàn)性詳解

    這篇文章主要介紹了Java并發(fā)編程-volatile可見(jiàn)性詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • Mybatis入門教程(四)之mybatis動(dòng)態(tài)sql

    Mybatis入門教程(四)之mybatis動(dòng)態(tài)sql

    這篇文章主要介紹了Mybatis入門教程(四)之mybatis動(dòng)態(tài)sql的相關(guān)資料,涉及到動(dòng)態(tài)sql及動(dòng)態(tài)sql的作用知識(shí),本文介紹的非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2016-09-09
  • MyBatis-Plus內(nèi)置接口方法的具體使用

    MyBatis-Plus內(nèi)置接口方法的具體使用

    java開(kāi)發(fā)應(yīng)用程序與數(shù)據(jù)庫(kù)交互使用比較多的就是mybatisPlus接口,本文主要介紹了MyBatis-Plus內(nèi)置接口方法的具體使用,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-12-12

最新評(píng)論

普兰店市| 辉南县| 宣化县| 富顺县| 太仆寺旗| 石河子市| 吉林省| 马鞍山市| 象州县| 黔西县| 桦甸市| 长海县| 英超| 华池县| 施秉县| 旺苍县| 东海县| 富锦市| 塔城市| 旺苍县| 辽源市| 云梦县| 石楼县| 洛隆县| 鹰潭市| 平定县| 内乡县| 顺义区| 甘洛县| 乌拉特后旗| 沙雅县| 阜新市| 高州市| 华坪县| 蓝山县| 舟曲县| 勃利县| 舞钢市| 和田县| 手游| 泊头市|