国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|JavaScript|易語言|

服務器之家 - 編程語言 - Java教程 - Java編程讀寫鎖詳解

Java編程讀寫鎖詳解

2021-05-23 15:24屌絲碼農 Java教程

本篇文章給大家詳細分享了Java編程讀寫鎖的相關原理以及知識點內容,有興趣的朋友們可以參考下。

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

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

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

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

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

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

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

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

簡單實現:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
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成員變量用于存儲數據和存取兩種操作。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
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();//釋放寫鎖
    }
 
  }
}

程序部分運行結果:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
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();
    }
  }
}

運行結果:

?
1
2
3
4
5
6
7
8
9
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]

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

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

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

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

再來看一個demo:

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
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的運用:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
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();
    }
  }
}

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
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)來判空

感謝大家對服務器之家的支持。

原文鏈接:https://www.cnblogs.com/pony1223/p/9428248.html

延伸 · 閱讀

精彩推薦
  • Java教程零基礎寫Java知乎爬蟲之進階篇

    零基礎寫Java知乎爬蟲之進階篇

    前面幾篇文章,我們都是簡單的實現了java爬蟲抓取內容的問題,那么如果遇到復雜情況,我們還能繼續那么做嗎?答案當然是否定的,之前的僅僅是入門篇...

    hebedich4632019-12-04
  • Java教程詳解Mybatis動態sql

    詳解Mybatis動態sql

    MyBatis的動態SQL是基于OGNL表達式的,它可以幫助我們方便的在SQL語句中實現某些邏輯。本文給大家介紹Mybatis動態sql小結,感興趣的朋友參考下 ...

    xiaoerduo123x4092020-04-18
  • Java教程java實現微信支付(服務端)

    java實現微信支付(服務端)

    這篇文章主要介紹了java實現微信支付,針對服務端進行代碼編寫,感興趣的小伙伴們可以參考一下 ...

    lijiao8802020-03-12
  • Java教程jpanel設置背景圖片的二個小例子

    jpanel設置背景圖片的二個小例子

    這篇文章主要介紹了jpanel設置背景圖片的二個小例子,實現了動態加載圖片做背景的方法,需要的朋友可以參考下 ...

    java教程網2532019-11-13
  • Java教程Servlet實現點擊計數器的方法

    Servlet實現點擊計數器的方法

    這篇文章主要介紹了Servlet實現點擊計數器的方法,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧...

    EasonJim4182020-12-21
  • Java教程淺談SpringBoot2.4 配置文件加載機制大變化

    淺談SpringBoot2.4 配置文件加載機制大變化

    這篇文章主要介紹了淺談SpringBoot2.4 配置文件加載機制大變化,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要...

    冷冷zz10462020-08-24
  • Java教程淺談Timer和TimerTask與線程的關系

    淺談Timer和TimerTask與線程的關系

    下面小編就為大家帶來一篇淺談Timer和TimerTask與線程的關系。小編覺得挺不錯的,現在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧...

    Java之家1542020-09-04
  • Java教程java連接mysql數據庫的方法

    java連接mysql數據庫的方法

    這篇文章主要為大家詳細介紹了java連接mysql數據庫的方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下 ...

    名字不好起啊4282020-09-23
主站蜘蛛池模板: 一级大毛片 | 午夜久久乐 | 国产精品一区二区免费 | 日韩欧美国产精品综合嫩v 日韩a∨精品日韩在线观看 | 精品一区二区av | 久久精品美女 | 午夜影院免费 | 欧美日韩一二区 | 国产成年人电影在线观看 | 国产精品毛片久久久久久久 | 亚洲国产精品福利 | 91精品国产亚洲 | 午夜精品一区 | 亚洲成人网一区 | 最近中文字幕免费 | 久久久久久成人 | 国产免费视频在线 | 亚洲第1页| 欧美激情一区二区三区 | 精品国产一区二区三区久久久蜜 | 成人免费毛片嘿嘿连载视频 | 色香蕉视频| 日韩三级电影免费观看 | 久久不卡 | 一区二区日韩 | 日韩成人在线播放 | 91精品国产综合久久久久 | 在线中文字幕第一页 | 亚洲福利一区二区 | 国产精品视频播放 | 亚洲国产aⅴ精品一区二区 少妇一级片免费看 | 亚洲激情在线播放 | 欧美freesex黑人又粗又大 | 一区二区在线不卡 | 亚洲精品在线观看网站 | 国产高清精品在线 | 精品国产区 | 欧美v片 | 一级毛片国产 | 欧美中文字幕一区 | 欧美a级免费看 |