Java并發(fā)編程之重入鎖與讀寫鎖
重入鎖
重入鎖,顧名思義,就是支持重進(jìn)入的鎖,它表示該鎖能夠支持一個(gè)線程對(duì)資源的重復(fù)加鎖。重進(jìn)入是指任意線程在獲取到鎖之后能夠再次獲取該鎖而不會(huì)被鎖阻塞,該特性的實(shí)現(xiàn)需要解決以下兩個(gè)問(wèn)題。
1、線程再次獲取鎖。鎖需要去識(shí)別獲取鎖的線程是否為當(dāng)前占據(jù)鎖的線程,如果是,則再次成功獲取。
2、鎖的最終釋放。線程重復(fù)n次獲取了鎖,隨后在第n次釋放該鎖后,其他線程能夠獲取到該鎖。鎖的最終釋放要求鎖對(duì)于獲取進(jìn)行計(jì)數(shù)自增,計(jì)數(shù)表示當(dāng)前鎖被重復(fù)獲取的次數(shù),而鎖被釋放時(shí),計(jì)數(shù)自減,當(dāng)計(jì)數(shù)等于0時(shí)表示鎖已經(jīng)成功釋放。
Java里面內(nèi)置鎖(synchronize)和Lock(ReentrantLock)都是可重入的
synchronized 實(shí)例
package com.home;
public class SynchronizedTest implements Runnable {
public synchronized void method1() {
System.out.println("method1獲得鎖,正常運(yùn)行!");
method2();
}
public synchronized void method2() {
System.out.println("method2獲得鎖,也正常運(yùn)行!");
}
@Override
public void run() {
method1();
}
public static void main(String[] args) {
SynchronizedTest st = new SynchronizedTest();
new Thread(st).start();
new Thread(st).start();
}
}
Lock 實(shí)例
package com.home;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockTest implements Runnable {
Lock lock = new ReentrantLock();
public void method1() {
lock.lock();
System.out.println("method1獲得鎖,正常運(yùn)行!");
method2();
lock.unlock();
}
public void method2() {
lock.lock();
System.out.println("method2獲得鎖,也正常運(yùn)行!");
lock.unlock();
}
@Override
public void run() {
method1();
}
public static void main(String[] args) {
LockTest lt = new LockTest();
new Thread(lt).start();
new Thread(lt).start();
}
}
兩個(gè)例子最后的結(jié)果都是正確的,結(jié)果如下:
method1獲得鎖,正常運(yùn)行! method2獲得鎖,也正常運(yùn)行! method1獲得鎖,正常運(yùn)行! method2獲得鎖,也正常運(yùn)行!
可重入鎖最大的作用是避免死鎖
讀寫鎖
讀寫鎖維護(hù)了一對(duì)相關(guān)的鎖,一個(gè)用于只讀操作,一個(gè)用于寫入操作。只要沒(méi)有writer,讀取鎖可以由多個(gè)reader線程同時(shí)保持。寫入鎖是獨(dú)占的。
可重入讀寫鎖 ReentrantReadWriteLock
ReentrantReadWriteLock對(duì)象提供了readLock()和writeLock()方法, 用于獲取讀取鎖和寫入鎖.
讀取鎖允許多個(gè)reader線程同時(shí)持有, 而寫入鎖最多只能有一個(gè)writter線程持有.
讀寫鎖的使用場(chǎng)合: 讀取共享數(shù)據(jù)的頻率遠(yuǎn)大于修改共享數(shù)據(jù)的頻率. 在上述場(chǎng)合下, 使用讀寫鎖控制共享資源的訪問(wèn), 可以提高并發(fā)性能.
如果一個(gè)線程已經(jīng)持有了寫入鎖, 則可以再持有讀寫鎖. 相反, 如果一個(gè)線程已經(jīng)持有了讀取鎖, 則在釋放該讀取鎖之前, 不能再持有寫入鎖.
可以調(diào)用寫入鎖的newCondition()方法獲取與該寫入鎖綁定的Condition對(duì)象, 此時(shí)與普通的互斥鎖并沒(méi)有什么區(qū)別. 但是調(diào)用讀取鎖的newCondition()方法將拋出異常.
例子
package com.home;
import java.util.Random;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
class ReadWrte {
// 共享數(shù)據(jù),可以多個(gè)線程讀數(shù)據(jù),只能有一個(gè)線程寫數(shù)據(jù)
private int data;
// 創(chuàng)建讀寫鎖
ReadWriteLock rwLock = new ReentrantReadWriteLock();
/**
* 讀數(shù)據(jù),上讀鎖
*/
public void get() {
// 讀鎖
rwLock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName() + ",Read!");
Thread.sleep((long) Math.random() * 1000);
System.out.println(Thread.currentThread().getName() + " 讀出的數(shù)據(jù)為:" +
this.getData());
} catch (Exception e) {
e.printStackTrace();
} finally {
rwLock.readLock().unlock();
}
}
/**
* 寫數(shù)據(jù),上寫鎖
*
* @param data
*/
public void put(int data) {
// 寫鎖
rwLock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName() + ",Write!");
Thread.sleep((long) Math.random() * 1000);
this.setData(data);
System.out.println(Thread.currentThread().getName() + " 寫入的數(shù)據(jù)為:" +
this.getData());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
rwLock.writeLock().unlock();
}
}
public int getData() {
return data;
}
public void setData(int data) {
this.data = data;
}
}
/**
* 測(cè)試類
*
* @author itmyhome
*
*/
public class ReadWriteLockTest {
/**
* @param args
*/
public static void main(String[] args) {
// 創(chuàng)建ReadWrte對(duì)象
final ReadWrte rw = new ReadWrte();
for (int i = 0; i < 10; i++) {
// 創(chuàng)建并啟動(dòng)10個(gè)讀線程
new Thread(new Runnable() {
@Override
public void run() {
rw.get();
}
}).start();
// 創(chuàng)建并啟動(dòng)10個(gè)寫線程
new Thread(new Runnable() {
@Override
public void run() {
// 寫入一個(gè)隨機(jī)數(shù)
rw.put(new Random().nextInt(8));
}
}).start();
}
}
}
輸出為
Thread-0,Read! Thread-4,Read! Thread-8,Read! Thread-12,Read! Thread-0 讀出的數(shù)據(jù)為:0 Thread-4 讀出的數(shù)據(jù)為:0 Thread-8 讀出的數(shù)據(jù)為:0 Thread-12 讀出的數(shù)據(jù)為:0 Thread-19,Write! Thread-19 寫入的數(shù)據(jù)為:5 Thread-7,Write! Thread-7 寫入的數(shù)據(jù)為:7 Thread-3,Write! Thread-3 寫入的數(shù)據(jù)為:4 Thread-16,Read! Thread-16 讀出的數(shù)據(jù)為:4 Thread-11,Write! Thread-11 寫入的數(shù)據(jù)為:0 Thread-15,Write! Thread-15 寫入的數(shù)據(jù)為:5 Thread-2,Read! Thread-2 讀出的數(shù)據(jù)為:5 Thread-17,Write! Thread-17 寫入的數(shù)據(jù)為:2 Thread-6,Read! Thread-6 讀出的數(shù)據(jù)為:2 Thread-1,Write! Thread-1 寫入的數(shù)據(jù)為:5 Thread-13,Write! Thread-13 寫入的數(shù)據(jù)為:4 Thread-9,Write! Thread-9 寫入的數(shù)據(jù)為:7 Thread-5,Write! Thread-5 寫入的數(shù)據(jù)為:2 Thread-10,Read! Thread-10 讀出的數(shù)據(jù)為:2 Thread-18,Read! Thread-14,Read! Thread-18 讀出的數(shù)據(jù)為:2 Thread-14 讀出的數(shù)據(jù)為:2
從圖中我們可以看出,可以多個(gè)線程同時(shí)讀,但只能一個(gè)線程寫,即寫數(shù)據(jù)和寫入數(shù)據(jù)一并完成。
總結(jié)
以上就是本文關(guān)于Java并發(fā)編程之重入鎖與讀寫鎖的全部?jī)?nèi)容,希望對(duì)大家有所幫助。歡迎各位參閱本站其他專題,有什么問(wèn)題可以隨時(shí)留言,小編會(huì)及時(shí)回復(fù)大家的。感謝大家對(duì)本站的支持。
- java并發(fā)編程StampedLock高性能讀寫鎖詳解
- java并發(fā)編程中ReentrantLock可重入讀寫鎖
- Java并發(fā)之搞懂讀寫鎖
- Java并發(fā)編程之ReadWriteLock讀寫鎖的操作方法
- Java并發(fā)編程之顯示鎖ReentrantLock和ReadWriteLock讀寫鎖
- Java多線程之ReentrantReadWriteLock源碼解析
- Java多線程 ReentrantReadWriteLock原理及實(shí)例詳解
- 一文了解Java讀寫鎖ReentrantReadWriteLock的使用
- 詳解Java?ReentrantReadWriteLock讀寫鎖的原理與實(shí)現(xiàn)
- Java并發(fā)讀寫鎖ReentrantReadWriteLock 使用場(chǎng)景
相關(guān)文章
mybatis實(shí)現(xiàn)mapper代理模式的方式
本文向大家講解mybatis的mapper代理模式,以根據(jù)ide值查詢單條數(shù)據(jù)為例編寫xml文件,通過(guò)mapper代理的方式進(jìn)行講解增刪改查,分步驟給大家講解的很詳細(xì),對(duì)mybatis mapper代理模式相關(guān)知識(shí)感興趣的朋友一起看看吧2021-06-06
微信APP支付(IOS手機(jī)端+java后臺(tái))版
這篇文章主要為大家詳細(xì)介紹了微信APP支付(IOS手機(jī)端+java后臺(tái))版,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-05-05
Leetcode常見(jiàn)鏈表問(wèn)題及代碼示例
這篇文章主要介紹了Leetcode常見(jiàn)鏈表問(wèn)題及代碼示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-11-11
java數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)順序表示例
這篇文章主要介紹了java數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)順序表示例,需要的朋友可以參考下2014-03-03
一步步教你把SpringBoot項(xiàng)目打包成Docker鏡像
Docker可以讓開(kāi)發(fā)者打包他們的應(yīng)用以及依賴包到一個(gè)輕量級(jí)、可移植的容器中,然后發(fā)布到任何流行的 Linux 機(jī)器上,也可以實(shí)現(xiàn)虛擬化,下面這篇文章主要給大家介紹了關(guān)于SpringBoot項(xiàng)目打包成Docker鏡像的相關(guān)資料,需要的朋友可以參考下2023-02-02
Java實(shí)現(xiàn)斷點(diǎn)下載服務(wù)端與客戶端的示例代碼
這篇文章主要為大家介紹了如何實(shí)現(xiàn)服務(wù)端(Spring Boot)與客戶端(Android)的斷點(diǎn)下載與下載續(xù)傳功能,文中的示例代碼講解詳細(xì),需要的可以參考一下2022-08-08

