眾所周知,synchronized是一款基于jvm(java虛擬機(jī))實(shí)現(xiàn)的鎖關(guān)鍵字,主要用來在高并發(fā)情況下保證程序的正確性。
鑒于現(xiàn)在大家的jdk版本都是升級(jí)到至少1.7了,因此我們主要談?wù)?.6+版本的synchronized關(guān)鍵字。
對(duì)于jdk1.6之后的synchronized關(guān)鍵字,不再是以前完全基于mutex(互斥量)的重量級(jí)鎖。而是加入了一些優(yōu)化。
首先,我們要知道一個(gè)常識(shí):鎖的概念是針對(duì)于線程的。只是針對(duì)于線程的!針對(duì)于線程的?。。?br>
所以實(shí)現(xiàn)鎖的意義,也是對(duì)于線程們而言的。
“在我執(zhí)行期間,你們不許動(dòng)我的東西”這就是它的霸道。然后他就會(huì)鎖住門,阻攔所有想要進(jìn)門的線程。直到他出門交出鑰匙為止。
總之,鎖是一種,在某個(gè)情境下,只讓某個(gè)線程獨(dú)占資源的一種手段。
那么實(shí)現(xiàn)方案呢?
我們會(huì)使用一個(gè)對(duì)象當(dāng)做門,當(dāng)一個(gè)線程執(zhí)行到synchronized(對(duì)象){內(nèi)容……}的時(shí)候,就是鎖了這個(gè)對(duì)象(門),只有它有鑰匙,之后再有別的線程執(zhí)行到這里,也進(jìn)不去,因?yàn)闆]有鑰匙,當(dāng)它執(zhí)行完{}里面的內(nèi)容之后,就會(huì)離開房間,交出鑰匙,下一個(gè)線程獲取鑰匙之后,才可以執(zhí)行它的操作。
那么實(shí)際中的具體實(shí)現(xiàn)呢?
首先我們要知道,在java中,任何一個(gè)對(duì)象都分為三部分,對(duì)象頭,數(shù)據(jù),填充位。
對(duì)于對(duì)象的控制我們可以利用對(duì)象頭實(shí)現(xiàn)。在對(duì)象頭上有個(gè)叫做mark Word的區(qū)域,在這里可以申請(qǐng)到兩個(gè)比特位的空間,我們給它打個(gè)鎖芯,把它作為專門的鎖控制位。就可以實(shí)現(xiàn)上面的過程了。
它的具體過程是這樣的:
1、當(dāng)一個(gè)線程進(jìn)入同步方法塊的時(shí)候,虛擬機(jī)首先會(huì)在線程的棧幀上建立一個(gè)名叫l(wèi)ock Record的空間,用于儲(chǔ)存鎖對(duì)象當(dāng)前的mark Word的拷貝。
2、將對(duì)象頭的Mark Word拷貝到線程的鎖記錄(Lock Recored)中。
3、拷貝成功后,虛擬機(jī)將使用CAS操作嘗試將對(duì)象的Mark Word更新為指向Lock Record的指針,并更新鎖位為偏向鎖。(指針指向了線程的lock Record,里面存的數(shù)據(jù)是對(duì)象頭的,所以從結(jié)構(gòu)上來說,是合理的)。
至此為止,這就算是完成了線程獲取對(duì)象的唯一的鑰匙的這一步
4、當(dāng)有新的線程進(jìn)入同步塊的時(shí)候,檢查Mark Word中的指針,如果是當(dāng)前線程,那么直接放行(當(dāng)前線程擁有這個(gè)對(duì)象的門的鑰匙,當(dāng)然可以無數(shù)次開門),如果不是,那么該線程開始自旋重新獲取鎖(有別的線程拿到了鑰匙,所以我就只能不斷地敲門,盼望下一秒它就可以結(jié)束,我瞬間搶到交還鑰匙),并且更新鎖位為輕量級(jí)鎖。
5、如果自旋一段時(shí)間之后,還是拿不到,就把Mark Word更新為指向Monitor的指針,并更新為重量級(jí)鎖。(多個(gè)線程一直自旋敲門,但是始終拿不到鑰匙,所以我干脆讓你們?nèi)孔枞辱€匙被交出了,你們?cè)賮頁(yè)?。自旋是一個(gè)主動(dòng)地行為,而阻塞喚醒是一個(gè)事件驅(qū)動(dòng)的行為)
銷毀就是釋放鎖,把鑰匙交出去。
根據(jù)加鎖對(duì)象的不同,synchronized關(guān)鍵字主要分為兩種級(jí)別的鎖:實(shí)例鎖,類鎖。
實(shí)例鎖是個(gè)體鎖,而類鎖是模具鎖。
這么解釋就很簡(jiǎn)單了。
把jvm的對(duì)象們,想象成一間大型公寓的房門。(因?yàn)閖ava對(duì)象的對(duì)象頭的特性,對(duì)象皆可為鎖)
一個(gè)線程說:我要鎖住所有型號(hào)為AAA-3的門。那么,所有符合AAA-3工業(yè)標(biāo)準(zhǔn)的門,就都會(huì)被鎖上。這就是類鎖。一個(gè)類加載器在一個(gè)java虛擬機(jī)上只能加載一個(gè)唯一類,它的所有實(shí)例都是根據(jù)類的結(jié)構(gòu)復(fù)制出來的。類,是一個(gè)工業(yè)化的模具。
另一個(gè)線程說:我要鎖住5樓第三間的門。這樣它鎖住的只是一個(gè)門,是一個(gè)個(gè)體,而不是一類門。所以它是實(shí)例鎖。
理論講了很多,接下來看例子吧。
第一個(gè)用法:實(shí)例鎖
顧名思義就是給實(shí)例加鎖,這樣的鎖,就是個(gè)體鎖。
代碼塊形式:手動(dòng)指定鎖實(shí)例對(duì)象;
Object lock1 = new Object();
Object lock2 = new Object();
@Override
public void run() {
//鎖住對(duì)象lock1
synchronized (lock1) {
System.out.print("線程:" + Thread.currentThread().getName() + "的lock1開始啦\n");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.print("線程:" + Thread.currentThread().getName() + "的lock1結(jié)束啦\n");
}
//鎖住對(duì)象lock2
synchronized (lock2) {
System.out.print("線程:" + Thread.currentThread().getName() + "的lock2開始啦\n");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.print("線程:" + Thread.currentThread().getName() + "的lock2結(jié)束啦\n");
}
}
方法鎖形式:synchronized修飾普通的方法,鎖對(duì)象默認(rèn)為this;
public synchronized void method(){
System.out.print("線程:" + Thread.currentThread().getName() + "的lock1開始啦\n");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.print("線程:" + Thread.currentThread().getName() + "的lock1結(jié)束啦\n");
}
public void run() {
method();
}
以上都是實(shí)例鎖,所以在測(cè)試類中,只要使用同一個(gè)類的單例runnable就可以讓線程1和線程2串行化執(zhí)行;
第二個(gè)用法:類鎖(class鎖)
給類,即class對(duì)象加鎖。
形式1:static 方法加鎖;
@Override
public void run() {
method();
}
//類鎖1
static synchronized void method(){
System.out.print("線程:" + Thread.currentThread().getName() + "的類鎖1開始啦\n");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.print("線程:" + Thread.currentThread().getName() + "的類鎖1結(jié)束啦\n");
}
形式2:synchronized(*.class);
@Override
public void run() {
synchronized (SynchronizedRequest2.class){
System.out.print("線程:" + Thread.currentThread().getName() + "的類鎖1開始啦\n");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.print("線程:" + Thread.currentThread().getName() + "的類鎖1結(jié)束啦\n");
}
}
static修飾的變量是類變量,修飾的方法是類級(jí)別的方法,它們都是屬于類的結(jié)構(gòu)。而class對(duì)象是類在代碼工程結(jié)構(gòu)中的實(shí)體表現(xiàn)。因此以上兩種方式是類鎖,也就是模具鎖。
當(dāng)然,類鎖是類級(jí)別的鎖,所以在測(cè)試類中,需要檢驗(yàn)的是同一個(gè)類的不同實(shí)例,看看有沒有被鎖住。
終わり