本篇來自慕課網(wǎng)"悟空"視頻的筆記。先簡單介紹一下synchronized您没。
在Java中,每個對象有且僅有一個同步鎖蒲列。不同的線程對同步鎖的訪問是互斥的忽媒,即同一時刻争拐,僅有一個線程可以得到該鎖。所以晦雨,我們可以以同步鎖為基礎(chǔ)架曹,實現(xiàn)多線程間對共享數(shù)據(jù)操作的可見性和原子性。使用同步鎖有幾種方式闹瞧,synchronized就是其中之一绑雄。
簡單介紹
synchronized是Java的一個關(guān)鍵字,是學(xué)習(xí)并發(fā)編程繞不開的一個知識點奥邮。它可以防止線程干擾和內(nèi)存一致性錯誤万牺,保證同一時刻最多只有一個線程執(zhí)行某段被鎖住的代碼罗珍,以保證并發(fā)安全。
用法
對象鎖
包括方法鎖(默認(rèn)對象為this當(dāng)前實例對象)和同步代碼塊鎖杏愤。
同步代碼塊鎖:
synchronized(this) {
System.out.println("對象鎖中的同步代碼塊鎖靡砌。線程名為:" + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "運行結(jié)束");
}
此處,通過this鎖住當(dāng)前對象珊楼。有一點要注意:此處如果是通過繼承Thread的方式創(chuàng)建了線程通殃,那么每次都會有不同的this對象。因此厕宗,此處的synchronized沒有作用画舌。
方法鎖:
public synchronized void test(){
System.out.println("對象鎖中的方法鎖。線程名為:" + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "運行結(jié)束");
}
方法鎖的默認(rèn)鎖對象為this已慢。
以上就是對象鎖的兩種形式曲聂,其中,在同步代碼塊鎖中佑惠,我們不僅可以使用this朋腋,還可以自定義一個鎖對象,比如:
Object obj = new Object();
synchronized(obj) {
System.out.println("對象鎖中的同步代碼塊鎖膜楷。線程名為:" + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "運行結(jié)束");
}
類鎖
指synchronized修飾靜態(tài)的方法或者指定鎖為Class對象旭咽。
靜態(tài)鎖
private static synchronized void test() {
System.out.println("我是類鎖中的靜態(tài)鎖。線程名為:" + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "運行結(jié)束");
}
與方法鎖不同的是赌厅,此方法為static修飾的靜態(tài)方法穷绵。
Class對象鎖
//此處假設(shè)我們有一個MyTest類
synchronized(MyTest.class) {
System.out.println("對象鎖中的同步代碼塊鎖。線程名為:" + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
以上就是類鎖的兩種形式特愿,與方法鎖不同的地方在于仲墨,類鎖的對象為當(dāng)前類。
注意:
1揍障、一把鎖只能同時被一個線程獲取目养,沒有拿到鎖的線程只能等待
2、每個實例都對應(yīng)有自己的一把鎖毒嫡,不同實例之間互不影響
3癌蚁、在方法執(zhí)行完畢或者拋出異常后,會釋放鎖
性質(zhì)
可重入性
指同一線程的外層函數(shù)獲得鎖后审胚,內(nèi)層函數(shù)可以直接再次獲取該鎖
好處:避免死鎖匈勋,提升封裝性
粒度:與線程相關(guān),與調(diào)用無關(guān)
不可中斷性
一旦一個鎖被其他線程獲得膳叨,其他的線程如果想獲得該鎖洽洁,就會等待或被阻塞,直到鎖被釋放菲嘴。如果鎖沒有被釋放饿自,等待的線程會永遠(yuǎn)等待下去汰翠。
加鎖和釋放鎖的原理
可重入原理:加鎖次數(shù)計數(shù)器。JVM負(fù)責(zé)跟蹤對象被加鎖的次數(shù)昭雌;線程第一次給對象加鎖的時候复唤,計數(shù)變?yōu)?.每當(dāng)這個相同的線程再次獲得該鎖時,計數(shù)器會遞增烛卧;每當(dāng)任務(wù)離開佛纫,計數(shù)遞減,當(dāng)技術(shù)為0的時候总放,鎖被釋放呈宇。
保證可見性的原理:內(nèi)存模型
通過反編譯看字節(jié)碼:javap -verbose hello.class
synchronized有個加鎖的monitorenter和解鎖的monitorexit,讀到指令局雄,會讓monitor計數(shù)器加一或者減一甥啄。
缺陷
1、效率低炬搭,鎖的釋放情況少蜈漓,一種是正常執(zhí)行任務(wù)完釋放,一種是異常JVM釋放宫盔,不能設(shè)置超時時間融虽;
2、不夠靈活飘言,讀的話可能不需要加鎖衣形,例如讀寫鎖就比較靈活驼侠;
3姿鸿、無法判斷狀態(tài),是否獲取到鎖