一、可重入
1,指的是同一個(gè)線程的外層函數(shù)(synchornized)獲得鎖之后,內(nèi)層函數(shù)(synchornized)可以再次獲取該鎖。
2,線程t1拿到了Monitor,想再次使用Monitor進(jìn)入下一個(gè)同步方法,如果要釋放掉Monitor,然后重新和其他線程競(jìng)爭(zhēng)Monitor就是不可重入;如果無需提前釋放Monitor,直接拿著Monitor去用,就是可重入。
3,可重入鎖也稱為遞歸鎖。
4,在java中,有兩個(gè)可重入鎖,一個(gè)就是synchornized關(guān)鍵字,還有一個(gè)就是ReentrantLock.
5,可重入鎖的好處: 避免死鎖,提升封裝性,避免了一次次的解鎖加鎖。
為什么能避免死鎖呢?
假設(shè)synchornized不具備可重入性,t1進(jìn)入一個(gè)synchornized方法1獲取到了Monitor,然后又要進(jìn)入下一次synchornized方法2,這個(gè)時(shí)候synchornized方法1沒有執(zhí)行完所以是不會(huì)釋放鎖的,這個(gè)時(shí)候因?yàn)椴痪邆淇芍厝胄?,所以進(jìn)入synchornized方法2又要求重新獲取Monitor,這樣就造成了死鎖。
所以synchornized的可重入性可以避免線程永遠(yuǎn)等待下去的現(xiàn)象。
6,可重入的scope:
java的synchornized關(guān)鍵字默認(rèn)加鎖的范圍是線程,但是linux中的pthread,它互斥體的默認(rèn)加鎖行為是以調(diào)用為粒度的。
線程而非調(diào)用(用三種情況來說明和pthread的區(qū)別)
情況1:證明同一個(gè)方法是可重入的
package synchornized;
/**
* Synchronized 可重入粒度測(cè)試:
* 證明同一個(gè)方法是可重入的
*/
public class SynchronizedRecursion10 implements Runnable{
int num = 0;
public static void main(String[] args) throws InterruptedException {
SynchronizedRecursion10 runnable = new SynchronizedRecursion10();
Thread thread0 = new Thread(runnable);
thread0.start();
thread0.join();
}
private synchronized void syn(){
System.out.println("get monitor");
num++;
if (num==3){
return;
}else {
syn();
}
}
@Override
public void run() {
syn();
}
}
情況2:證明可重入不要求是同一個(gè)方法
package synchornized;
/**
* Synchronized 可重入粒度測(cè)試:
* 證明可重入不要求是同一個(gè)方法
*/
public class SynchronizedRecursion11 implements Runnable{
int num = 0;
public static void main(String[] args) throws InterruptedException {
SynchronizedRecursion11 runnable = new SynchronizedRecursion11();
Thread thread0 = new Thread(runnable);
thread0.start();
thread0.join();
}
private synchronized void syn1(){
System.out.println("enter syn1");
syn2();
}
private synchronized void syn2(){
System.out.println("enter syn2");
}
@Override
public void run() {
syn1();
}
}
情況3:證明可重入不要求是同一個(gè)類中的方法:子類調(diào)用父類的方法
package synchornized;
/**
* Synchronized 可重入粒度測(cè)試:
* 證明可重入不要求是同一個(gè)類中的方法:子類調(diào)用父類的方法
*/
public class SynchronizedRecursionSuperClass12 {
public synchronized void syn2(){
System.out.println("我是父類");
}
}
class SynchronizedRecursion12 extends SynchronizedRecursionSuperClass12 implements Runnable{
public static void main(String[] args) throws InterruptedException {
SynchronizedRecursion12 runnable = new SynchronizedRecursion12();
Thread thread0 = new Thread(runnable);
thread0.start();
thread0.join();
}
@Override
public synchronized void syn2(){
System.out.println("我是子類");
super.syn2();
}
@Override
public void run() {
syn2();
}
}
synchornized關(guān)鍵字的粒度肯定不是調(diào)用層面的,是線程級(jí)別的。在同一個(gè)線程中,它如果已經(jīng)拿到了一把鎖,然后又想接著使用這把鎖去訪問其他方法,只要該方法需要的鎖依然是我手中的這把鎖,那么它的可重入的性質(zhì)就會(huì)被激發(fā)出來,就不需要顯式地釋放鎖再重新獲取鎖,它可以直接拿著鎖去完成訪問。
二,不可中斷
一旦這個(gè)鎖已經(jīng)被別人獲得了,如果我還想獲得,我只能選擇等待或者阻塞,直到別的線程釋放這個(gè)鎖。如果別人永遠(yuǎn)不釋放該鎖,那么我只能永遠(yuǎn)地等待下去。
相比之下,在未來會(huì)介紹的Lock類,可以擁有中斷的能力,第一點(diǎn),如果我覺得我等的時(shí)間太長(zhǎng)了,有權(quán)中斷現(xiàn)在已經(jīng)獲取到鎖的線程的執(zhí)行;第二點(diǎn),如果我覺得我等待的時(shí)間太長(zhǎng)了不想再繼續(xù)等了,也可以退出。
Lock相對(duì)于Synchronized更加靈活,但是在編碼的時(shí)候注意點(diǎn)也更多。