? “可重入鎖”的概念是:自己可以再次獲取自己的內(nèi)部鎖,比如有1個(gè)線程獲得了某個(gè)對(duì)象的鎖,此時(shí)這個(gè)對(duì)象鎖還沒(méi)有釋放,當(dāng)其再次想要獲取這個(gè)對(duì)象的鎖的時(shí)候還是可以獲取的,如果不可鎖重入的話,就會(huì)造成死鎖。
? “可重入鎖”的最大作用就是避免死鎖
1.2 分析
我們知道,在程序中,是無(wú)法顯式釋放對(duì)同步監(jiān)視器的鎖的,而會(huì)在如下幾個(gè)情況下釋放鎖:
① 當(dāng)前線程的同步方法、代碼塊執(zhí)行結(jié)束的時(shí)候釋放
② 當(dāng)前線程在同步方法、同步代碼塊遇到break、return終止該代碼塊或方法的時(shí)候釋放
③ 出現(xiàn)未處理的error或exception導(dǎo)致異常結(jié)束的時(shí)候釋放
④ 程序執(zhí)行了同步對(duì)象wait方法,當(dāng)前線程暫停,釋放鎖
那么,在上面的程序中,當(dāng)線程進(jìn)入同步方法method1時(shí)獲得Service1的對(duì)象鎖,但是在執(zhí)行method1的時(shí)候調(diào)用了同步方法method2,按照正常情況,在執(zhí)行同步方法method2同樣需要獲得對(duì)象鎖,但是根據(jù)上面釋放鎖的條件,此時(shí)method1的對(duì)象鎖還沒(méi)有釋放,此時(shí)就會(huì)造成死鎖,無(wú)法繼續(xù)執(zhí)行method2。但是通過(guò)上面代碼的執(zhí)行結(jié)果來(lái)看,能夠正常執(zhí)行method2和method3,這就說(shuō)明,在一個(gè)Synchronized修飾的方法或代碼塊的內(nèi)部調(diào)用本類的其他Synchronized修飾的方法或代碼塊時(shí),是永遠(yuǎn)可以得到鎖的。
1.3 父子可繼承性
可重入鎖支持在父子類繼承的環(huán)境中,示例代碼如下:
public class Service2 {
public int i = 10;
public synchronized void mainMethod(){
i--;
System.out.println("main print i=" i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class Service3 extends Service2 {
public synchronized void subMethod(){
try{
while (i>0){
i--;
System.out.println("sub print i= " i);
Thread.sleep(100);
this.mainMethod();
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
public class MyThread extends Thread {
@Override
public void run(){
Service3 service3 = new Service3();
service3.subMethod();
}
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
}
}
運(yùn)行結(jié)果如下:
此程序說(shuō)明,當(dāng)存在父子類繼承關(guān)系時(shí),子類完全可以通過(guò)“可重入鎖”調(diào)用父類的同步方法。
2. 出現(xiàn)異常,鎖自動(dòng)釋放
當(dāng)一個(gè)線程執(zhí)行的代碼出現(xiàn)異常時(shí),其所持有的鎖會(huì)自動(dòng)釋放。
驗(yàn)證代碼如下:
public class Service4 {
public synchronized void testMethod(){
if(Thread.currentThread().getName().equals("a")){
System.out.println("ThreadName= " Thread.currentThread().getName() " run beginTime=" System.currentTimeMillis());
int i=1;
while (i == 1){
if(("" Math.random()).substring(0,8).equals("0.123456")){
System.out.println("ThreadName= " Thread.currentThread().getName() " run exceptionTime=" System.currentTimeMillis());
//Integer.parseInt("a");
}
}
}else{
System.out.println("Thread B run time= " System.currentTimeMillis());
}
}
}
public class ThreadA extends Thread{
private Service4 service4;
public ThreadA(Service4 service4){
this.service4 = service4;
}
@Override
public void run(){
service4.testMethod();
}
}
public class ThreadB extends Thread{
private Service4 service4;
public ThreadB(Service4 service4){
this.service4 = service4;
}
@Override
public void run(){
service4.testMethod();
}
}
public class Main {
public static void main(String[] args) {
try {
Service4 service4 = new Service4();
ThreadA a = new ThreadA(service4);
a.setName("a");
a.start();
Thread.sleep(500);
ThreadB b = new ThreadB(service4);
b.setName("b");
b.start();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
注意Service4類中Integer.parseInt(“a”);此時(shí)處于被注釋狀態(tài),運(yùn)行結(jié)果如下:
由于a線程沒(méi)有錯(cuò)誤,while(true),此時(shí)a線程處于無(wú)限循環(huán)狀態(tài),鎖一直被a占用,b線程無(wú)法獲得鎖,即無(wú)法執(zhí)行b線程。
將Service4類中Integer.parseInt(“a”);解開注釋,執(zhí)行的結(jié)果如下:
當(dāng)a線程發(fā)生錯(cuò)誤時(shí),b線程獲得鎖從而執(zhí)行,由此可見,當(dāng)方法出現(xiàn)異常時(shí),鎖自動(dòng)釋放。
3. 將任意對(duì)象作為監(jiān)視器
java支持對(duì)“任意對(duì)象”作為“對(duì)象監(jiān)視器”來(lái)實(shí)現(xiàn)同步的功能。這個(gè)“任意對(duì)象”大多數(shù)是實(shí)例變量及方法的參數(shù),使用格式為synchronized(非this對(duì)象x)同步代碼塊。
示例代碼如下:
public class StringLock {
private String lock = "lock";
public void method(){
synchronized (lock){
try {
System.out.println("當(dāng)前線程: " Thread.currentThread().getName() "開始");
Thread.sleep(1000);
System.out.println("當(dāng)前線程: " Thread.currentThread().getName() "結(jié)束");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
final StringLock stringLock = new StringLock();
new Thread(new Runnable() {
@Override
public void run() {
stringLock.method();
}
},"t1").start();
new Thread(new Runnable() {
@Override
public void run() {
stringLock.method();
}
},"t2").start();
}
}
運(yùn)行結(jié)果如下:
鎖非this對(duì)象具有一定的優(yōu)點(diǎn):如果在一個(gè)類中有很多個(gè)synchronized方法,這時(shí)雖然能實(shí)現(xiàn)同步,但會(huì)受到阻塞,所以影響運(yùn)行效率;但如果使用同步代碼塊鎖非this對(duì)象,則synchronized(非this)代碼塊中的程序與同步方法是異步的,不予其他鎖this同步方法爭(zhēng)搶this鎖,則可大大提高運(yùn)行效率。
4. 同步不具有繼承性
父類的同步方法,在子類中重寫后不加同步關(guān)鍵字,是不會(huì)同步的,所以還得在子類的方法中添加synchronized關(guān)鍵字。
推薦學(xué)習(xí):Java視頻教程
更多關(guān)于云服務(wù)器,域名注冊(cè),虛擬主機(jī)的問(wèn)題,請(qǐng)?jiān)L問(wèn)西部數(shù)碼官網(wǎng):m.ps-sw.cn