|
一、作用 1. 多線程同步代碼,保證方法或者代碼塊在運(yùn)行時(shí),同一時(shí)刻只有一個(gè)線程可以進(jìn)入到臨界區(qū)(互斥性) 2. 保證線程間共享變量的修改及時(shí)可見(jiàn)(可見(jiàn)性) a. 當(dāng)線程獲取鎖時(shí),線程的本地變量無(wú)效,需要從主存中獲取共享變量的值 b. 線程釋放鎖時(shí),線程的本地變量被刷新到主存中 3. 有效解決重排序問(wèn)題(有序性) 二、用法 1. Java中的每個(gè)對(duì)象都可以作為鎖,獲取的鎖都是對(duì)象 2. 修飾函數(shù),即普通同步方法,鎖是當(dāng)前類(lèi)的實(shí)例對(duì)象 public void synchronized A(){} 3. 靜態(tài)同步方法,鎖是當(dāng)前類(lèi)的class對(duì)象 public static void synchronized A(){} 4. 修飾函數(shù)內(nèi)的語(yǔ)句塊,即同步代碼塊,鎖是括號(hào)中的對(duì)象 synchronized(obj){} 5. 每個(gè)對(duì)象只有一個(gè)鎖(lock)與之關(guān)聯(lián) 6. 作用域 a. 某個(gè)對(duì)象實(shí)例內(nèi)的方法,不同對(duì)象的實(shí)例內(nèi)的方法不相干擾,其他線程可以同時(shí)訪問(wèn)相同類(lèi)的其他對(duì)象實(shí)例中的synchronized方法 b. 某個(gè)類(lèi)的范圍,一般是靜態(tài)方法,可以防止多個(gè)線程同時(shí)訪問(wèn)相同類(lèi)中的synchronized方法 三、原理 1. java示例代碼,同步靜態(tài)方法 public class SynchronizedTest {
private static Object object = new Object();
public static void main(String[] args) throws Exception{
synchronized(object) {
}
}
public static synchronized void m() {}
}2. 代碼->字節(jié)碼->反編譯后的代碼 public static void main(java.lang.String[]) throws java.lang.Exception;
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=3, args_size=1
0: getstatic #2 // Field object:Ljava/lang/Object
3: dup
4: astore_1
5: monitorenter //監(jiān)視器進(jìn)入,獲取鎖
6: aload_1
7: monitorexit //監(jiān)視器退出,釋放鎖
8: goto 16
11: astore_2
12: aload_1
13: monitorexit
14: aload_2
15: athrow
16: return
public static synchronized void m();
descriptor: ()V
flags: ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED
Code:
stack=0, locals=0, args_size=0
0: return
LineNumberTable:
line 9: 03. 同步代碼塊使用monitorenter和monitorexit指令實(shí)現(xiàn) 4. 同步方法使用修飾符ACC_SYNCHRONIZED實(shí)現(xiàn) 5. 無(wú)論哪種實(shí)現(xiàn),本質(zhì)上都是對(duì)monitor的獲取,這個(gè)過(guò)程是互斥的 四、鎖的狀態(tài) 1. 對(duì)象頭: a. 對(duì)象在內(nèi)存中分為3部分:對(duì)象頭、實(shí)例數(shù)據(jù)、對(duì)齊填充 b. 對(duì)象頭可以記錄對(duì)象的狀態(tài):偏向鎖、輕量級(jí)鎖、重量級(jí)鎖 2. monitor: a. 線程私有的數(shù)據(jù)結(jié)構(gòu),每個(gè)線程有一個(gè)monitor列表 b. JVM記錄獲取monitor鎖的線程唯一id,確保一次只有一個(gè)線程執(zhí)行 3. 偏向鎖狀態(tài): a. 如果一個(gè)線程獲取了鎖,那么鎖就進(jìn)入了偏向狀態(tài) b. 當(dāng)這個(gè)線程再次請(qǐng)求鎖時(shí),不需要再做同步,就可以獲取鎖,避免了申請(qǐng)鎖的操作,優(yōu)化了性能 c. 適用于沒(méi)有激烈競(jìng)爭(zhēng)鎖的情況,不僅沒(méi)有多線程競(jìng)爭(zhēng),而且總是由同一個(gè)線程獲取鎖 4. 輕量級(jí)鎖狀態(tài): a. 獲取偏向鎖失敗后,會(huì)膨脹為輕量級(jí)鎖 b. 適用于交替執(zhí)行同步塊的情況 5. 重量級(jí)鎖狀態(tài): a. 獲取輕量級(jí)鎖失敗后,會(huì)膨脹為重量級(jí)鎖 b. 此時(shí)所有線程都會(huì)被鎖住,當(dāng)前獲取鎖的線程釋放后,其他線程才被喚醒 五、等待通知機(jī)制,即wait(),notify(),notifyall()進(jìn)行線程間通信 1. 代碼 public class WaitNotify {
static boolean flag = true;
static Object lock = new Object();
public static void main(String[] args) throws InterruptedException {
Thread A = new Thread(new Wait(), "wait thread");
A.start();
TimeUnit.SECONDS.sleep(2);
Thread B = new Thread(new Notify(), "notify thread");
B.start();
}
static class Wait implements Runnable {
@Override
public void run() {
synchronized (lock) {
while (flag) {
try {
System.out.println(Thread.currentThread() " flag is true");
lock.wait();
} catch (InterruptedException e) {
}
}
System.out.println(Thread.currentThread() " flag is false");
}
}
}
static class Notify implements Runnable {
@Override
public void run() {
synchronized (lock) {
flag = false;
lock.notifyAll();
try {
TimeUnit.SECONDS.sleep(7);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}2. 使用wait(),notify(),notifyAll()時(shí),需要先對(duì)對(duì)象加鎖 3. 調(diào)用wait()方法后,線程會(huì)釋放鎖,線程狀態(tài)RUNNING->WAITING,將線程移動(dòng)到等待隊(duì)列 4. notify()/notifyAll()時(shí),等待線程不會(huì)立即從wait()返回,需要當(dāng)前線程釋放鎖之后,才有機(jī)會(huì)獲取鎖返回 5. notify()將一個(gè)等待隊(duì)列里的線程,移動(dòng)到同步隊(duì)列,線程狀態(tài)WAITING->BLOCKED 6. notifyAll()將所有等待隊(duì)列里的線程,移動(dòng)到同步隊(duì)列,線程狀態(tài)WAITING->BLOCKED 7. 線程只有獲取了鎖,才能從wait()返回 |
|
|
來(lái)自: 印度阿三17 > 《開(kāi)發(fā)》