可重入鎖:
??可重入鎖藐鹤,也叫遞歸鎖韭赘,指定是同一線程 外層函數(shù)獲得鎖之后踩窖,內(nèi)層遞歸函數(shù)仍可以再次獲取鎖而不會出現(xiàn)死鎖诺擅。
public class WhatReentrant{
public static void main(String[] args){
new Thread(() -> {
synchronized (this) {
System.out.println("第一次獲取鎖,這個鎖是:" + this);
int index = 1;
while (true) {
synchronized (this) {
System.out.println("第" + (++index) + "次獲取鎖谴分,這個鎖是:" + this);
}
if (index == 10) {
break;
}
}
}
}).start();
}
}
public class WhatReentrant2 {
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
new Thread(() -> {
try {
lock.lock();
System.out.println("第1次獲取鎖锈麸,這個鎖是:" + lock);
int index = 1;
while (true) {
try {
lock.lock();
System.out.println("第" + (++index) +"次獲取鎖,這個鎖是:" + lock);
try {
Thread.sleep(new Random().nextInt(200));
} catch (InterruptedException e) {
e.printStackTrace();
}
if (index == 10) {
break;
}
} finally {
lock.unlock();
}
}
} finally {
lock.unlock();
}
}).start();
}
}
public class Restaurant {
private Lock windows = new ReentrantLock();
public void getMeals() throws Exception {
try {
windows.lock();
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "打飯");
} finally {
windows.unlock();
}
}
public void getSoup() throws Exception {
try {
windows.lock();
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "打湯");
} finally {
windows.unlock();
}
}
public void today() throws Exception {
try {
windows.lock();
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "打飯");
getMeals();
getSoup();
} finally {
windows.unlock();
}
}
public static void main(String[] args) {
Restaurant test = new Restaurant();
new Thread(() -> {
try {
test.today();
} catch (Exception e) {
e.printStackTrace();
}
}, "我").start();
new Thread(() -> {
try {
test.getSoup();
} catch (Exception e) {
e.printStackTrace();
}
}, "某人").start();
new Thread(() -> {
try {
test.getMeals();
} catch (Exception e) {
e.printStackTrace();
}
}, "另一個人").start();
}
}
輸出:
我打飯
我打飯
我打湯
某人打湯
另一個人打飯
可以發(fā)現(xiàn)以上都沒有發(fā)生死鎖狸剃,可以多次獲取相同的鎖
可重入鎖有:
- synchronized
- ReentrantLock
注意:
??ReentrantLock和synchronized不一樣掐隐,需要手動釋放鎖,所以使用ReentrantLock的時候一定要手動釋放鎖钞馁,并且加鎖的次數(shù)和釋放次數(shù)要一樣
讀寫鎖
- 為什么需要讀寫鎖虑省?
??在多線程的環(huán)境下,對同一份數(shù)據(jù)進(jìn)行讀寫僧凰,會涉及到線程安全問題探颈。比如在一個線程讀取數(shù)據(jù)的時候,另一個線程正在寫數(shù)據(jù)训措,而導(dǎo)致前后數(shù)據(jù)的不一致性伪节;一個線程在寫數(shù)據(jù)的時候光羞,另一個線程也在寫數(shù)據(jù),同樣會導(dǎo)致數(shù)據(jù)的不一致性怀大。
??這時可以在讀寫方法中加入互斥鎖纱兑,任何時候只能允許一個線程的一個讀寫操作,這樣是可以解決問題化借,但是效率卻大打折扣了潜慎。因?yàn)樵谡鎸?shí)的業(yè)務(wù)場景中,讀取數(shù)據(jù)的操作次數(shù)通常高于寫入數(shù)據(jù)的操作蓖康,而線程之間的讀讀操作不涉及線程安全問題铐炫,沒有必要加入互斥鎖,只要在讀-寫蒜焊,寫寫期間上上鎖即可倒信。
讀寫鎖機(jī)制:
- 讀-讀不互斥
- 讀-寫互斥
- 寫-寫互斥
public class ReadWriteLockTest {
private static ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
private static ReentrantReadWriteLock.ReadLock readLock = rwLock.readLock(); //拿到讀鎖
private static ReentrantReadWriteLock.WriteLock writeLock = rwLock.writeLock(); //寫鎖
public static void read() {
readLock.lock();
try{
System.out.println("read method ================");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}finally {
readLock.unlock();
}
}
// public static void read1() {
public static void write() {
writeLock.lock();
try{
System.out.println("read method >>>>>>>>>>>>>>>>>>>>>");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}finally {
writeLock.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
ReadWriteLockTest.write();
}).start();
new Thread(() -> {
ReadWriteLockTest.read();
}).start();
Thread.sleep(10000);
}
}