最簡單的東西,每每包含了最複雜的實現,由於須要爲上層的存在提供一個穩定的基礎,Object做爲java中全部對象的基類,其存在的價值不言而喻,其中wait和notify方法的實現多線程協做提供了保證。html
public class WaitTestDemo { public static void main(String[] args) { Message msg = new Message("process it"); Waiter waiter = new Waiter(msg); new Thread(waiter,"waiterThread").start(); Waiter waiter1 = new Waiter(msg); new Thread(waiter1, "waiter1Thread").start(); Notifier notifier = new Notifier(msg); new Thread(notifier, "notifierThread").start(); System.out.println("All the threads are started"); } public static class Message { private String msg; public Message(String str){ this.msg=str; } public String getMsg() { return msg; } public void setMsg(String str) { this.msg=str; } } public static class Waiter implements Runnable{ private Message msg; public Waiter(Message m){ this.msg=m; } @Override public void run() { String name = Thread.currentThread().getName(); synchronized (msg) { try{ System.out.println(name+" waiting to get notified at time:"+System.currentTimeMillis()); msg.wait(); }catch(InterruptedException e){ e.printStackTrace(); } System.out.println(name+" waiter thread got notified at time:"+System.currentTimeMillis()); //process the message now System.out.println(name+" processed: "+msg.getMsg()); } } } public static class Notifier implements Runnable { private Message msg; public Notifier(Message msg) { this.msg = msg; } @Override public void run() { String name = Thread.currentThread().getName(); System.out.println(name+" started"); try { Thread.sleep(1000); synchronized (msg) { msg.setMsg(name+" Notifier work done"); msg.notify(); msg.notify(); //msg.notifyAll(); } } catch (InterruptedException e) { e.printStackTrace(); } } } }
Output:java
All the threads are started waiterThread waiting to get notified at time:1572344152693 waiter1Thread waiting to get notified at time:1572344152693 notifierThread started waiterThread waiter thread got notified at time:1572344153705 waiterThread processed: notifierThread Notifier work done waiter1Thread waiter thread got notified at time:1572344153706 waiter1Thread processed: notifierThread Notifier work done
也可使用notifyAll,輸出爲:node
All the threads are started waiterThread waiting to get notified at time:1572344222162 waiter1Thread waiting to get notified at time:1572344222162 notifierThread started waiter1Thread waiter thread got notified at time:1572344223175 waiter1Thread processed: notifierThread Notifier work done waiterThread waiter thread got notified at time:1572344223177 waiterThread processed: notifierThread Notifier work done
發現最後喚醒的順序顛倒了編程
執行完notify方法,並不會立馬喚醒等待線程,在notify方法後面加一段sleep代碼就能夠看到效果,若是線程執行完notify方法以後sleep 5s,在這段時間內,線程waiterThread1依舊持有monitor,線程waiterThread只能繼續等待;數組
在Java中,synchronized
有兩種使用形式,同步方法和同步代碼塊。代碼以下:多線程
public class SynchronizedTest { public synchronized void doSth(){ System.out.println("Hello World"); } public void doSth1(){ synchronized (SynchronizedTest.class){ System.out.println("Hello World"); } } }
咱們先來使用Javap來反編譯以上代碼,結果以下(部分無用信息過濾掉了):併發
public synchronized void doSth(); descriptor: ()V flags: ACC_PUBLIC, ACC_SYNCHRONIZED Code: stack=2, locals=1, args_size=1 0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #3 // String Hello World 5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return public void doSth1(); descriptor: ()V flags: ACC_PUBLIC Code: stack=2, locals=3, args_size=1 0: ldc #5 // class com/hollis/SynchronizedTest 2: dup 3: astore_1 4: monitorenter 5: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 8: ldc #3 // String Hello World 10: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 13: aload_1 14: monitorexit 15: goto 23 18: astore_2 19: aload_1 20: monitorexit 21: aload_2 22: athrow 23: return
反編譯後,咱們能夠看到Java編譯器爲咱們生成的字節碼。在對於doSth
和doSth1
的處理上稍有不一樣。也就是說。JVM對於同步方法和同步代碼塊的處理方式不一樣。oracle
對於同步方法,JVM採用ACC_SYNCHRONIZED
標記符來實現同步。 對於同步代碼塊。JVM採用monitorenter
、monitorexit
兩個指令來實現同步。jvm
關於這部份內容,在JVM規範中也能夠找到相關的描述。ide
方法級的同步是隱式的。同步方法的常量池中會有一個ACC_SYNCHRONIZED
標誌。當某個線程要訪問某個方法的時候,會檢查是否有ACC_SYNCHRONIZED
,若是有設置,則須要先得到監視器鎖,而後開始執行方法,方法執行以後再釋放監視器鎖。這時若是其餘線程來請求執行方法,會由於沒法得到監視器鎖而被阻斷住。值得注意的是,若是在方法執行過程當中,發生了異常,而且方法內部並無處理該異常,那麼在異常被拋到方法外面以前監視器鎖會被自動釋放。
同步代碼塊使用monitorenter
和monitorexit
兩個指令實現。 The Java® Virtual Machine Specification 中有關於這兩個指令的介紹:
大體內容以下: 能夠把執行monitorenter
指令理解爲加鎖,執行monitorexit
理解爲釋放鎖。 每一個對象維護着一個記錄着被鎖次數的計數器。未被鎖定的對象的該計數器爲0,當一個線程得到鎖(執行monitorenter
)後,該計數器自增變爲 1 ,當同一個線程再次得到該對象的鎖的時候,計數器再次自增。當同一個線程釋放鎖(執行monitorexit
指令)的時候,計數器再自減。當計數器爲0的時候。鎖將被釋放,其餘線程即可以得到鎖。
同步方法經過
ACC_SYNCHRONIZED
關鍵字隱式的對方法進行加鎖。當線程要執行的方法被標註上ACC_SYNCHRONIZED
時,須要先得到鎖才能執行該方法。同步代碼塊經過
monitorenter
和monitorexit
執行來進行加鎖。當線程執行到monitorenter
的時候要先得到所鎖,才能執行後面的方法。當線程執行到monitorexit
的時候則要釋放鎖。每一個對象自身維護這一個被加鎖次數的計數器,當計數器數字爲0時表示能夠被任意線程得到鎖。當計數器不爲0時,只有得到鎖的線程才能再次得到鎖。便可重入鎖。
每一個對象分爲三塊區域:對象頭、實例數據和對齊填充。
級鎖定、重量級鎖定、GC標記、可偏向)下對象的存儲內容以下表所示。
Synchronized一般被稱爲重量級鎖,可是1.6以後對其進行優化,新增了輕量級鎖和偏向鎖,這裏重點說下重量級鎖,隨後對Synchronized的優化簡單介紹下。
從對象頭的存儲內容能夠看出鎖的狀態都保存在對象頭中,Synchronized也不例外,當其從輕量級鎖膨脹爲重量級鎖時,鎖標識位爲10,其中指針指向的是monitor對象(也稱爲管程或監視器鎖)的起始地址。
關於Synchronized的實如今java對象頭裏較爲簡單,只是改變一下標識位,並將指針指向monitor對象的起始地址,其實現的重點是monitor對象。
在HotSpot虛擬機中,monitor採用ObjectMonitor實現。
一般所說的對象的內置鎖,是對象頭Mark Word中的重量級鎖指針指向的monitor對象,該對象是在HotSpot底層C++語言編寫的(openjdk裏面看),簡單看一下代碼:
//結構體以下 ObjectMonitor::ObjectMonitor() { _header = NULL; _count = 0; _waiters = 0, _recursions = 0; //線程的重入次數 _object = NULL; _owner = NULL; //標識擁有該monitor的線程 _WaitSet = NULL; //等待線程組成的雙向循環鏈表,_WaitSet是第一個節點 _WaitSetLock = 0 ; _Responsible = NULL ; _succ = NULL ; _cxq = NULL ; //多線程競爭鎖進入時的單向鏈表 FreeNext = NULL ; _EntryList = NULL ; //_owner從該雙向循環鏈表中喚醒線程結點,_EntryList是第一個節點 _SpinFreq = 0 ; _SpinClock = 0 ; OwnerIsThread = 0 ; }
ObjectMonitor隊列之間的關係轉換能夠用下圖表示:
既然提到了_waitSet和_EntryList(_cxq隊列後面會說),那就看一下底層的wait和notify方法
//1.調用ObjectSynchronizer::wait方法 void ObjectSynchronizer::wait(Handle obj, jlong millis, TRAPS) { /*省略 */ //2.得到Object的monitor對象(即內置鎖) ObjectMonitor* monitor = ObjectSynchronizer::inflate(THREAD, obj()); DTRACE_MONITOR_WAIT_PROBE(monitor, obj(), THREAD, millis); //3.調用monitor的wait方法 monitor->wait(millis, true, THREAD); /*省略*/ } //4.在wait方法中調用addWaiter方法 inline void ObjectMonitor::AddWaiter(ObjectWaiter* node) { /*省略*/ if (_WaitSet == NULL) { //_WaitSet爲null,就初始化_waitSet _WaitSet = node; node->_prev = node; node->_next = node; } else { //不然就尾插 ObjectWaiter* head = _WaitSet ; ObjectWaiter* tail = head->_prev; assert(tail->_next == head, "invariant check"); tail->_next = node; head->_prev = node; node->_next = head; node->_prev = tail; } } //5.而後在ObjectMonitor::exit釋放鎖,接着 thread_ParkEvent->park 也就是wait
總結:經過object得到內置鎖(objectMonitor),經過內置鎖將Thread封裝成OjectWaiter對象,而後addWaiter將它插入以_waitSet爲首結點的等待線程鏈表中去,最後釋放鎖。
//1.調用ObjectSynchronizer::notify方法 void ObjectSynchronizer::notify(Handle obj, TRAPS) { /*省略*/ //2.調用ObjectSynchronizer::inflate方法 ObjectSynchronizer::inflate(THREAD, obj())->notify(THREAD); } //3.經過inflate方法獲得ObjectMonitor對象 ObjectMonitor * ATTR ObjectSynchronizer::inflate (Thread * Self, oop object) { /*省略*/ if (mark->has_monitor()) { ObjectMonitor * inf = mark->monitor() ; assert (inf->header()->is_neutral(), "invariant"); assert (inf->object() == object, "invariant") ; assert (ObjectSynchronizer::verify_objmon_isinpool(inf), "monitor is inva;lid"); return inf } /*省略*/ } //4.調用ObjectMonitor的notify方法 void ObjectMonitor::notify(TRAPS) { /*省略*/ //5.調用DequeueWaiter方法移出_waiterSet第一個結點 ObjectWaiter * iterator = DequeueWaiter() ; //6.後面省略是將上面DequeueWaiter尾插入_EntrySet的操做 /**省略*/ }
總結:經過object得到內置鎖(objectMonitor),調用內置鎖的notify方法,經過_waitset結點移出等待鏈表中的首結點,將它置於_EntrySet中去,等待獲取鎖。注意:notifyAll根據policy不一樣可能移入_EntryList或者_cxq隊列中,此處不詳談。