引用自:http://blog.iluckymeeting.com/2018/01/06/threadandlockthree/
什么是synchronized加鎖
synchronized是Java語(yǔ)言的關(guān)鍵字受裹,它加的是語(yǔ)言級(jí)的鎖统阿,被synchronized聲明的代碼塊被加鎖后將具有以下特性:
- 原子互斥性 被synchronized聲明的代碼塊一次只能被一個(gè)線程執(zhí)行,其它線程必須等獲得鎖的線程執(zhí)行完成后才能再次加鎖執(zhí)行同步代碼塊
- 可見(jiàn)性 鎖是加在某一個(gè)對(duì)象實(shí)例上的甜害,如果有多個(gè)同步代碼塊在同一個(gè)對(duì)象上加鎖僧凤,則各同步代碼塊中做的修改將相互可見(jiàn)
在JDK1.6以前synchronized通過(guò)對(duì)象監(jiān)視器(Monitor)來(lái)加重量級(jí)鎖岛请,經(jīng)過(guò)JDK1.6的優(yōu)化引入了偏向鎖和輕量級(jí)鎖秕硝,提高了synchronized加鎖的效率。
synchronized加鎖的優(yōu)勢(shì)與不足
- synchronized加的是非公平鎖捷枯,效率上優(yōu)于公平鎖滚秩,由JVM保證所有申請(qǐng)鎖的線程都有機(jī)會(huì)獲得鎖
- synchronized鎖的釋放是由JVM管理的,即使是同步代碼塊中拋出了異常淮捆,也能保證鎖最終會(huì)被釋放郁油,這樣可以避免人為失誤造成鎖未被釋放從而導(dǎo)致死鎖
- synchronized加鎖后,JVM在做線程轉(zhuǎn)儲(chǔ)時(shí)會(huì)包含鎖信息争剿,這一點(diǎn)對(duì)于調(diào)試工作非常有幫助
- 等待獲得sychronized鎖的線程無(wú)法被中斷已艰,也不能通過(guò)投票獲得鎖痊末,這將消耗更多的資源來(lái)做線程調(diào)度
synchronized加鎖用法
方式 | 作用范圍 | 作用對(duì)象 |
---|---|---|
代碼塊 | 代碼塊 | 調(diào)用代碼塊的對(duì)象 |
方法 | 方法 | 調(diào)用方法的對(duì)象 |
靜態(tài)方法 | 靜態(tài)方法 | 類(lèi)的所有對(duì)象 |
類(lèi) | 括號(hào)里的內(nèi)容 | 類(lèi)的所有對(duì)象 |
對(duì)象實(shí)例 | 對(duì)象本身 | 具體對(duì)象 |
代碼塊加鎖
public class Demo {
public void invoke() {
synchronized (this) {
String threadName = Thread.currentThread().getName();
System.out.println(threadName + " started");
try {
Thread.sleep(5 * 1000);
} catch (InterruptedException e) {
}
System.out.println(threadName + " ended");
}
}
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
int final_i = i;
Thread t = new Thread(() -> {
Demo demo = new Demo();
Thread.currentThread().setName("thread" + final_i);
demo.invoke();
});
t.start();
}
}
}
運(yùn)行結(jié)果如下:
thread0 started
thread1 started
thread2 started
thread2 ended
thread0 ended
thread1 ended
從運(yùn)行結(jié)果上可以看出蚕苇,3個(gè)線程的執(zhí)行是沒(méi)有阻塞的,代碼塊上的synchronized鎖沒(méi)起作用凿叠,為什么呢涩笤?因?yàn)閟ynchronized聲明的代碼塊聲明時(shí)使用的this對(duì)象嚼吞,所以這個(gè)代碼塊的鎖是加在Demo類(lèi)的對(duì)象上的,而這3個(gè)線程各自實(shí)例化了一個(gè)Demo類(lèi)的對(duì)象蹬碧,每個(gè)線程申請(qǐng)的鎖都是加在不同對(duì)象上的鎖舱禽,所以不會(huì)阻塞。
將上面的代碼修改一下恩沽,讓3個(gè)線程申請(qǐng)同一個(gè)Demo類(lèi)實(shí)例對(duì)象上的鎖:
public static void main(String[] args) {
Demo demo = new Demo();
for (int i = 0; i < 3; i++) {
int final_i = i;
Thread t = new Thread(() -> {
Thread.currentThread().setName("thread" + final_i);
demo.invoke();
});
t.start();
}
}
再次運(yùn)行一下誊稚,結(jié)果如下:
thread0 started
thread0 ended
thread2 started
thread2 ended
thread1 started
thread1 ended
可以看出修改后3個(gè)線程是依次執(zhí)行的,synchronized鎖生效了
方法加鎖
public class Demo {
public synchronized void invoke() {
String threadName = Thread.currentThread().getName();
System.out.println(threadName + " started");
try {
Thread.sleep(5 * 1000);
} catch (InterruptedException e) {
}
System.out.println(threadName + " ended");
}
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
int final_i = i;
Thread t = new Thread(() -> {
Thread.currentThread().setName("thread" + final_i);
Demo demo = new Demo();
demo.invoke();
});
t.start();
}
}
}
運(yùn)行后的輸出:
thread0 started
thread1 started
thread2 started
thread1 ended
thread0 ended
thread2 ended
從輸出上可以看出synchronized加鎖并沒(méi)有影響3個(gè)線程的執(zhí)行罗心,為什么呢里伯?因?yàn)榉椒暶魃霞觭ynchronized鎖時(shí),加鎖對(duì)象是Demo類(lèi)實(shí)例化后的對(duì)象渤闷,在上面3個(gè)線程中每個(gè)線程都實(shí)例化了一個(gè)Demo類(lèi)的對(duì)象疾瓮,所以鎖是加在不同的對(duì)象上的,每個(gè)線程申請(qǐng)的都是不同的鎖,自然不會(huì)影響三個(gè)線程的執(zhí)行;將上面的代碼修改一下飒箭,使3個(gè)線程使用同一個(gè)Demo類(lèi)的實(shí)例對(duì)象狼电,再次運(yùn)行看一下結(jié)果。將main方法修改如下弦蹂,其它代碼不變:
public static void main(String[] args) {
Demo demo = new Demo();
for (int i = 0; i < 3; i++) {
int final_i = i;
Thread t = new Thread(() -> {
Thread.currentThread().setName("thread" + final_i);
demo.invoke();
});
t.start();
}
}
運(yùn)行結(jié)果如下:
thread0 started
thread0 ended
thread2 started
thread2 ended
thread1 started
thread1 ended
從運(yùn)行結(jié)果上可以看出肩碟,3個(gè)線程是依次執(zhí)行的,synchronized加的鎖起作用了
靜態(tài)方法加鎖
public class Demo {
public synchronized static void invoke() {
String threadName = Thread.currentThread().getName();
System.out.println(threadName + " started");
try {
Thread.sleep(5 * 1000);
} catch (InterruptedException e) {
}
System.out.println(threadName + " ended");
}
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
int final_i = i;
Thread t = new Thread(() -> {
Thread.currentThread().setName("thread" + final_i);
Demo.invoke();
});
t.start();
}
}
}
運(yùn)行結(jié)果如下:
thread0 started
thread0 ended
thread2 started
thread2 ended
thread1 started
thread1 ended
由結(jié)果可以看出3個(gè)線程是順序執(zhí)行的凸椿,這是因?yàn)榧渔i的方法是靜態(tài)方法腾务,所有的方法調(diào)用申請(qǐng)加鎖是使用的同一個(gè)鎖,不論Demo類(lèi)實(shí)例化了多少個(gè)對(duì)象削饵。
類(lèi)加鎖
還是上面用到的代碼塊加鎖岩瘦,修改成如下:
public class Demo {
public void invoke() {
synchronized (Demo.class) {
String threadName = Thread.currentThread().getName();
System.out.println(threadName + " started");
try {
Thread.sleep(5 * 1000);
} catch (InterruptedException e) {
}
System.out.println(threadName + " ended");
}
}
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
int final_i = i;
Thread t = new Thread(() -> {
Demo demo = new Demo();
Thread.currentThread().setName("thread" + final_i);
demo.invoke();
});
t.start();
}
}
}
運(yùn)行結(jié)果如下:
thread0 started
thread0 ended
thread2 started
thread2 ended
thread1 started
thread1 ended
可以看出3個(gè)線程是按順序執(zhí)行的,鎖生效了窿撬,可是3個(gè)線程都實(shí)例化了一個(gè)自己的demo對(duì)象启昧,為什么會(huì)這樣?這是因?yàn)榧渔i的對(duì)象變了劈伴,
synchronized(this)
這種加鎖方式會(huì)把鎖加在Demo類(lèi)的實(shí)例對(duì)象上密末,如果創(chuàng)建多個(gè)Demo類(lèi)的實(shí)例對(duì)象就會(huì)相應(yīng)的在每個(gè)對(duì)象上加鎖,而這里用的是
synchronized(Demo.class)
這種加鎖方式跛璧,此時(shí)無(wú)論Demo被實(shí)例出多少個(gè)對(duì)象严里,它們用的都是同一把鎖,所以3個(gè)線程是順序執(zhí)行的追城。
對(duì)象實(shí)例加鎖
public class Demo {
private ReentrantLocker lock = new ReentrantLocker();
public void invoke() {
synchronized (lock) {
String threadName = Thread.currentThread().getName();
System.out.println(threadName + " started");
try {
Thread.sleep(5 * 1000);
} catch (InterruptedException e) {
}
System.out.println(threadName + " ended");
}
}
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
int final_i = i;
Thread t = new Thread(() -> {
Demo demo = new Demo();
Thread.currentThread().setName("thread" + final_i);
demo.invoke();
});
t.start();
}
}
}
運(yùn)行結(jié)果如下:
thread2 started
thread1 started
thread0 started
thread2 ended
thread1 ended
thread0 ended
可以看出3個(gè)線程并沒(méi)有競(jìng)爭(zhēng)同一個(gè)鎖刹碾,這是因?yàn)槊總€(gè)Demo實(shí)例對(duì)象都有一個(gè)lock對(duì)象,所以每個(gè)線程都申請(qǐng)了不同的鎖;
修改一下代碼座柱,將lock對(duì)象聲明改成下面這樣
private static ReentrantLocker lock = new ReentrantLocker();
再次運(yùn)行結(jié)果如下:
thread0 started
thread0 ended
thread2 started
thread2 ended
thread1 started
thread1 ended
3個(gè)線程是按順序執(zhí)行的了迷帜,為什么呢物舒?因?yàn)閘ock對(duì)象被聲明成了static靜態(tài)對(duì)象,雖然Demo被實(shí)例出了多個(gè)對(duì)象戏锹,但是它們使用的lock對(duì)象是同一個(gè)冠胯,所以3個(gè)線程申請(qǐng)的鎖是加在同一個(gè)對(duì)象上的。