日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【JUC并发编程09】读写锁

發布時間:2025/3/20 编程问答 15 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【JUC并发编程09】读写锁 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

    • 9 讀寫鎖
      • 9.1 悲觀鎖和樂觀鎖
      • 9.2 表鎖|行鎖|讀鎖|寫鎖
      • 9.3 讀寫鎖概述
      • 9.4 讀寫鎖的演變
      • 9.5 鎖降級的必要性

9 讀寫鎖

9.1 悲觀鎖和樂觀鎖

悲觀鎖:顧名思義,它是干什么都很悲觀,所以在操作的時候,每次都先上鎖,使用時解鎖

樂觀鎖:它很樂觀,多線程,并不上鎖,但是會發生線程安全問題,通過比較版本號來同步

9.2 表鎖|行鎖|讀鎖|寫鎖

表鎖:整個表操作,不會發生死鎖

行鎖:每個表中的單獨一行進行加鎖,會發生死鎖

讀鎖:共享鎖(可以有多個人讀),會發生死鎖

寫鎖:獨占鎖(只能有一個人寫),會發生死鎖

比如說,

1線程修改時候,需要等待2線程讀之后

2線程修改時候,需要等待1線程讀之后

所以,1-2 線程發生死鎖

9.3 讀寫鎖概述

讀寫鎖:一個資源可以被多個讀線程訪問,也可以被一個寫線程訪問,但不能同時存在讀寫線程,讀寫互斥,讀讀共享

讀寫鎖 ReentrantReadWriteLock
讀鎖為 ReentrantReadWriteLock.ReadLock,readLock()方法
寫鎖為 ReentrantReadWriteLock.WriteLock,writeLock()方法

創建讀寫鎖對象 private ReadWriteLock rwLock = new ReentrantReadWriteLock();
寫鎖 加鎖 rwLock.writeLock().lock();,解鎖為rwLock.writeLock().unlock();
讀鎖 加鎖 rwLock.readLock().lock();,解鎖為rwLock.readLock().unlock();

在不加讀寫鎖的情況下:

class MyCache{// 需要模仿從Map中取對象,所以先穿件一個map對象private volatile Map<String, Object> map = new HashMap<>();// 放數據public void put(String key, Object value) {try {System.out.println(Thread.currentThread().getName()+"正在寫操作"+key);// 暫停一會TimeUnit.MICROSECONDS.sleep(300);// 放數據map.put(key, value);System.out.println(Thread.currentThread().getName()+"寫完了"+key);} catch (InterruptedException e) {e.printStackTrace();}}// 取數據public void get(String key) {try {System.out.println(Thread.currentThread().getName()+"正在取操作"+key);// 暫停一會TimeUnit.MICROSECONDS.sleep(300);// 放數據map.get(key);System.out.println(Thread.currentThread().getName()+"取完了"+key);} catch (InterruptedException e) {e.printStackTrace();}} }public class ReadWriteLockTest {public static void main(String[] args) {MyCache myCache = new MyCache();for (int i = 1; i <= 6; i++) {final int num = i;new Thread(()->{myCache.put(String.valueOf(num),String.valueOf(num));},String.valueOf(i)).start();}for (int i = 1; i <= 6; i++) {final int num = i;new Thread(()->{myCache.get(String.valueOf(num));},String.valueOf(i)).start();}} }

輸出結果如下:

1正在寫操作1
4正在寫操作4
3正在寫操作3
2正在寫操作2
5正在寫操作5
6正在寫操作6
1正在取操作1
2正在取操作2
3正在取操作3
4正在取操作4
5正在取操作5
6正在取操作6
2取完了2
2寫完了2
5寫完了5
5取完了5
6取完了6
3寫完了3
4寫完了4
1寫完了1
6寫完了6
4取完了4
3取完了3
1取完了1

很顯然,線程在寫操作的時候,有線程在讀操作,這可能會出現臟數據

加上讀寫鎖:

class MyCache{// 需要模仿從Map中取對象,所以先穿件一個map對象private volatile Map<String, Object> map = new HashMap<>();// 創建讀寫鎖private ReadWriteLock rwlock = new ReentrantReadWriteLock();// 放數據public void put(String key, Object value) {// 添加寫鎖rwlock.writeLock().lock();try {System.out.println(Thread.currentThread().getName()+"正在寫操作"+key);// 暫停一會TimeUnit.MICROSECONDS.sleep(300);// 放數據map.put(key, value);System.out.println(Thread.currentThread().getName()+"寫完了"+key);} catch (InterruptedException e) {e.printStackTrace();} finally {// 釋放寫鎖rwlock.writeLock().unlock();}}// 取數據public void get(String key) {// 添加讀鎖rwlock.readLock().lock();;try {System.out.println(Thread.currentThread().getName()+"正在取操作"+key);// 暫停一會TimeUnit.MICROSECONDS.sleep(300);// 放數據map.get(key);System.out.println(Thread.currentThread().getName()+"取完了"+key);} catch (InterruptedException e) {e.printStackTrace();} finally {// 釋放讀鎖rwlock.readLock().unlock();}} }

得到的結果為:

2正在寫操作2
2寫完了2
1正在寫操作1
1寫完了1
3正在寫操作3
3寫完了3
4正在寫操作4
4寫完了4
5正在寫操作5
5寫完了5
6正在寫操作6
6寫完了6
1正在取操作1
2正在取操作2
5正在取操作5
3正在取操作3
6正在取操作6
4正在取操作4
1取完了1
3取完了3
2取完了2
4取完了4
6取完了6
5取完了5

9.4 讀寫鎖的演變

無鎖:多線程搶奪資源

synchronized和ReentrantLock,都是獨占,每次只可以一個操作,不能共享

**ReentrantReadWriteLock,**讀讀可以共享,提升性能,但是不能多人寫。缺點:造成死鎖(一直讀,不能寫),讀進程不能寫,寫進程可以讀。

寫鎖降級為讀鎖(一般等級寫鎖高于讀鎖)

具體第四步演練的代碼

獲取寫鎖->獲取讀鎖->釋放寫鎖->釋放讀鎖

//演示讀寫鎖降級 public class Demo1 {public static void main(String[] args) {//可重入讀寫鎖對象ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();ReentrantReadWriteLock.ReadLock readLock = rwLock.readLock();//讀鎖ReentrantReadWriteLock.WriteLock writeLock = rwLock.writeLock();//寫鎖//鎖降級//1 獲取寫鎖writeLock.lock();System.out.println("---write");//2 獲取讀鎖readLock.lock();System.out.println("---read");//3 釋放寫鎖writeLock.unlock();//4 釋放讀鎖readLock.unlock();} }

倘若先讀再寫,由于讀寫鎖的特點,在讀鎖后不允許寫,所以會被阻塞

9.5 鎖降級的必要性

鎖降級中讀鎖的獲取是否必要呢?答案是必要的。

我們在使用讀寫鎖時遵守下面的獲取規則

1.如果有一個線程已經占用了讀鎖,則此時其他線程如果要申請讀鎖,可以申請成功。

2.如果有一個線程已經占用了讀鎖,則此時其他線程如果要申請寫鎖,則申請寫鎖的線程會一直等待釋放讀鎖,因為讀寫不能同時操作。

3.如果有一個線程已經占用了寫鎖,則此時其他線程如果申請寫鎖或者讀鎖,都必須等待之前的線程釋放寫鎖,同樣也因為讀寫不能同時,并且兩個線程不應該同時寫

主要是為了保證數據的可見性,如果當前線程不獲取讀鎖而是直接釋放寫鎖, 假設此刻另一個線程(記作線程T)獲取了寫鎖并修改了數據,那么當前線程無法感知線程T的數據更新。如果當前線程獲取讀鎖,即遵循鎖降級的步驟,則線程T將會被阻塞,直到當前線程使用數據并釋放讀鎖之后,線程T才能獲取寫鎖進行數據更新。

這時因為可能存在一個事務線程不希望自己的操作被別的線程中斷,而這個事務操作可能分成多部分操作更新不同的數據(或表)甚至非常耗時。如果長時間用寫鎖獨占,顯然對于某些高響應的應用是不允許的,所以在完成部分寫操作后,退而使用讀鎖降級,來允許響應其他進程的讀操作。只有當全部事務完成后才真正釋放鎖。

所以總結下鎖降級的意義應該就是:在一邊讀一邊寫的情況下提高性能。

如果是讀之后再寫,執行不了 因為讀鎖權限小于寫鎖 需要讀完之后釋放讀鎖,在進行寫鎖

總結

以上是生活随笔為你收集整理的【JUC并发编程09】读写锁的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。