一涡贱、sleep & wait
首先從一個面試問題開篇:我們經(jīng)常用的sleep()
& wait()
方法有什么不同洛巢?
基于我自己的知識范圍,我給出以下回答:
sleep
方法是Thread
類的靜態(tài)方法,而wait
方法是Object
類下的非靜態(tài)方法sleep
方法 執(zhí)行時不會釋放monitor渣玲;而wait
方法在執(zhí)行時會釋放monitor,并將當前線程加入到等待monitor的wait set
中弟晚。- 啥時候想讓當前線程休眠了忘衍,直接
Thread.sleep(x)
就好,不需要依賴monitor卿城;但wait
需要- 通常情況下淑履,
sleep
方法的執(zhí)行線程不需要別人喚醒,但wait()
通常需要別人notify()
后才能喚醒接著執(zhí)行藻雪,當然wait(x)
方法除外秘噪。
對于第二點,通過以下代碼進行驗證勉耀。
關于sleep指煎,我們常規(guī)的sleep寫法:
public static void main(String[] args) {
try {
Thread.sleep(30000);
} catch (InterruptedException e) {
e.printStackTrace();
}
此時通過jstack查看線程狀態(tài)發(fā)現(xiàn):[main線程并沒有在等待monitor]
"main" #1 prio=5 os_prio=31 tid=0x00007fe24d002000 nid=0x1903 waiting on condition [0x0000700006f50000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
關于wait,我們通過以下代碼來對wait方法進行說明:
public static void main(String[] args) {
Stream.of("T1", "T2").forEach(name ->
new Thread(name) {
@Override
public void run() {
// m1();
m2();
}
}.start()
);
}
public static void m1() {
synchronized (LOCK) {
try {
System.out.println("The Thread " + Thread.currentThread().getName() + " enter.");
Thread.sleep(20000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void m2() {
synchronized (LOCK) {
try {
System.out.println("The Thread " + Thread.currentThread().getName() + " enter.");
LOCK.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
首先注掉m1()
便斥,放開m2()
至壤。運行程序,得到運行結果:
The Thread T1 enter.
The Thread T2 enter.
這就說明枢纠,在T1獲得LOCK進入synchronized代碼塊之后像街,通過wait將鎖進行了釋放,使得T2嘗試獲取LOCK時能夠成功獲取,并進入synchronzied代碼塊執(zhí)行了println語句镰绎。
那如果注掉m2()
放開m1()
呢脓斩?結果如下:
The Thread T1 enter.
通過jstack發(fā)現(xiàn),由于T1手里握著鎖畴栖,仍處在synchronized代碼塊中随静,因此T2在嘗試獲取鎖時因為無法獲取而進入阻塞狀態(tài):
"T2" #14 prio=5 os_prio=31 tid=0x00007fd96902a000 nid=0x5b03 waiting for monitor entry [0x000070000be19000]
java.lang.Thread.State: BLOCKED (on object monitor)
"T1" #13 prio=5 os_prio=31 tid=0x00007fd968895000 nid=0xa903 waiting on condition [0x000070000bd16000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
關于第三點,做以下測試吗讶,即去掉獲取鎖的代碼為:
public static void m2() {
// synchronized (LOCK) {
try {
System.out.println("The Thread " + Thread.currentThread().getName() + " enter.");
LOCK.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
// }
}
執(zhí)行結果:
Exception in thread "T1" Exception in thread "T2" java.lang.IllegalMonitorStateException
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:502)
所以結論為:需要先獲取鎖才能執(zhí)行wait(包括notify燎猛、notifyAll等)操作
二、synchronized
通過以上代碼我們注意到照皆,synchronized使用的是一個實例化之后的Object類的對象LOCK作為鎖重绷,那就聊聊關于synchronized的幾種寫法,以及這幾種寫法分別把鎖加到了哪里膜毁。
① 同步代碼塊
final Object LOCK = new Object();
synchronized (LOCK) {
try {
System.out.println("The Thread " + Thread.currentThread().getName() + " enter.");
Thread.sleep(20000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
這種寫法就是很明顯的用LOCK
對象作為鎖用于線程同步
② 同步方法
被synchronized所修飾的方法的所在類:
public class SychronizedStatic {
public synchronized void m4() {
System.out.println("m4 " + Thread.currentThread().getName());
try {
Thread.sleep(10_000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public synchronized void m5() {
System.out.println("m5 " + Thread.currentThread().getName());
try {
Thread.sleep(10_000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
測試代碼:
public static void main(String[] args) {
SychronizedStatic sychronizedStatic = new SychronizedStatic();
new Thread("T4") {
@Override
public void run() {
sychronizedStatic.m4();
}
}.start();
new Thread("T5") {
@Override
public void run() {
sychronizedStatic.m5();
}
}.start();
}
這種寫法也比較好理解论寨,作為鎖的對象就是調(diào)用者,即sychronizedStatic
爽茴。
③ 靜態(tài)同步方法
被synchronized所修飾的靜態(tài)方法的所在類:
public class SychronizedStatic {
public synchronized static void m1() {
System.out.println("m1 " + Thread.currentThread().getName());
try {
Thread.sleep(10_000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public synchronized static void m2() {
System.out.println("m2 " + Thread.currentThread().getName());
try {
Thread.sleep(10_000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void m3() {
synchronized (SychronizedStatic.class) {
System.out.println("m3 " + Thread.currentThread().getName());
try {
Thread.sleep(10_000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
測試方法:
public static void main(String[] args) {
new Thread("T1") {
@Override
public void run() {
SychronizedStatic.m1();
}
}.start();
new Thread("T2") {
@Override
public void run() {
SychronizedStatic.m2();
}
}.start();
}
測試結果如下:
m1 T1
這就說明葬凳,m2()
和m3()
阻塞在了SychronizedStatic.class
上,也就是說被synchronized修飾的靜態(tài)方法室奏,在做線程同步時火焰,加鎖是加在靜態(tài)方法所在的靜態(tài)類Class對象上的。
三胧沫、 瞎扯
sleep & wait & synchronize昌简,這都是java開發(fā)最基本的東西,但是如果稍微細問問的話绒怨,還真不一定理解的那么透徹纯赎。因此,學無止境南蹂,要融會貫通~