線程間通信
public class SynchronizedTest {
public static void main(String[] args) {
synchronized (SynchronizedTest.class){
}
m();
}
public synchronized static void m(){
}
}
在編譯後的同一目錄
javap -v SynchronizedTest
Last modified 2015-12-2; size 587 bytes
MD5 checksum faa56efff9ff6a17c0e345ae6503e899
Compiled from "SynchronizedTest.java"
public class concurrent.SynchronizedTest
SourceFile: "SynchronizedTest.java"
minor version: 0
major version: 50
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #4.#23 // java/lang/Object."<init>":()V
#2 = Class #24 // concurrent/SynchronizedTest
#3 = Methodref #2.#25 // concurrent/SynchronizedTest.m:()V
#4 = Class #26 // java/lang/Object
#5 = Utf8 <init>
#6 = Utf8 ()V
#7 = Utf8 Code
#8 = Utf8 LineNumberTable
#9 = Utf8 LocalVariableTable
#10 = Utf8 this
#11 = Utf8 Lconcurrent/SynchronizedTest;
#12 = Utf8 main
#13 = Utf8 ([Ljava/lang/String;)V
#14 = Utf8 args
#15 = Utf8 [Ljava/lang/String;
#16 = Utf8 StackMapTable
#17 = Class #15 // "[Ljava/lang/String;"
#18 = Class #26 // java/lang/Object
#19 = Class #27 // java/lang/Throwable
#20 = Utf8 m
#21 = Utf8 SourceFile
#22 = Utf8 SynchronizedTest.java
#23 = NameAndType #5:#6 // "<init>":()V
#24 = Utf8 concurrent/SynchronizedTest
#25 = NameAndType #20:#6 // m:()V
#26 = Utf8 java/lang/Object
#27 = Utf8 java/lang/Throwable
{
public concurrent.SynchronizedTest();
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>
":()V
4: return
LineNumberTable:
line 6: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lconcurrent/SynchronizedTest;
public static void main(java.lang.String[]);
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=3, args_size=1
0: ldc_w #2 // class concurrent/SynchronizedTe
st
3: dup
4: astore_1
5: monitorenter 監視器進入獲取鎖
6: aload_1
7: monitorexit 監視器退出 釋放鎖
8: goto 16
11: astore_2
12: aload_1
13: monitorexit
14: aload_2
15: athrow
16: invokestatic #3 // Method m:()V
19: return
Exception table:
from to target type
6 8 11 any
11 14 11 any
LineNumberTable:
line 10: 0
line 12: 6
line 14: 16
line 15: 19
LocalVariableTable:
Start Length Slot Name Signature
0 20 0 args [Ljava/lang/String;
StackMapTable: number_of_entries = 2
frame_type = 255 /* full_frame */
offset_delta = 11
locals = [ class "[Ljava/lang/String;", class java/lang/Object ]
stack = [ class java/lang/Throwable ]
frame_type = 250 /* chop */
offset_delta = 4
public static synchronized void m();
//方法修飾符
flags: ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED
Code:
stack=0, locals=0, args_size=0
0: return
LineNumberTable:
line 20: 0
}
對於同步快的實現使用了monitorenter 和 monitorexit指令 同步方法則是依靠方法修飾符上的ACC_SYNCHRONIZED 來完成 無論採用哪種方式 其本質都是對一個對象的監視器的獲取 這個過程是排他的
也就是同一時刻只能有一個線程能獲取到sync關鍵字所保護的監視器.
等待/通知機制
等待 通知機制的相關方法是任意java對象都具備的,因爲這些方法定義在java.lang.Object上
方法名稱 | 描述說明 |
---|---|
notify() | 通知一個在對象上等待的線程 使其從wait()方法返回 而返回的前提是該線程獲取到了對象的鎖 |
notifyAll() | 通知所有等待在該對象上的線程 |
wait() | 調用該方法的線程進入Waiting狀態 只有等待另外線程的通知或中斷纔會返回 需要注意 調用wait 方法後 會釋放對象的鎖 |
wait(long) | 超時等待一段時間 這裏的參數時間是毫秒 也就是等待長達n毫秒 如果沒有通知則超時返回 |
wait(long,int) | 對於超時時間更細粒度的控制 可以達到納秒 |
等待/通知機制 是指一個線程a調用了對象O的wait()方法進入等待狀態 而另一個線程b調用了對象o的notify()或者NotifyAll()方法 線程a收到通知後從對象o的wait方法中返回 執行後續的操作 上述倆個線程通過對象o來完成交互 而對象上的wait和notify的關係就如同開關信號一樣 用來完成等待方和通知方之間的交互
static Object lock = new Object();
static volatile boolean flag = true;
public static void main(String[] args) throws InterruptedException {
new Thread(new Wait(),"wait-thread").start();
TimeUnit.SECONDS.sleep(2);
new Thread(new Notify(),"notify-thread").start();
}
static class Wait implements Runnable {
@Override
public void run() {
//獲取鎖 擁有鎖的Monitor
synchronized (lock){
//當條件不滿足時 繼續wait 同時釋放了lock的鎖
while (flag){
System.out.println("flag = true need wait @ " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("flag = false get here @ " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
}
}
}
static class Notify implements Runnable {
@Override
public void run() {
//枷鎖 擁有lock的monitor
synchronized (lock){
//獲取lock的鎖 然後進行通知 通知時不會釋放lock的鎖 直到當前線程釋放了 lock後 WaitThread 才從wait方法中返回
lock.notify();
System.out.println("hold lock @ " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
try {
TimeUnit.SECONDS.sleep(5);
flag = false;
System.out.println("hold lock @ " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
知識點:
1)使用wait() notify()或者nofityAll()時需要先對調用對象加鎖
2) 調用wait()方法後 線程由running變爲waiting 並將當前線程放置到對象的等待隊列
3)notify()或notifyAll()方法刁永紅 等待線程依舊不會從wait()方法返回 需要調用notify()或者notifyAll()的線程釋放鎖之後 等待線程纔有機會從wait()返回
4) notify()方法將等待隊列中的一個等待線程從等待隊列中移動到同步隊列中 而notifyAll()是將等待隊列中的所有線程全部移動到同步隊列 被移動的線程狀態由waiting變爲blocked
5)從wait()返回的前提是獲得了調用對象的鎖
等待/通知模式的經典範式
等待方:
1)獲取對象的鎖
2) 如果條件不滿足 那麼調用對象的wait方法 被通知後仍要檢查條件、
3) 條件滿足則執行對應的邏輯
僞碼:
synchronized(對象){
while(條件不滿足){
對象.wait()
}
對應的處理邏輯
}
通知方:
1)獲得對象的鎖
2)改變條件
3)通知所有等待在對象上的線程
僞碼:
synchronized(對象){
改變條件
對象.notifyAll();
}