標籤:16px 基本 zed top font cond 文法 lease lin
JAVA的進程同步是通過synchronized()來實現的,須要說明的是,JAVA的synchronized()方法相似於作業系統概念中的相互排斥記憶體塊。在JAVA中的Object類型中。都是帶有一個記憶體鎖的,在有線程擷取該記憶體鎖後。其它線程無法訪問該記憶體。從而實現JAVA中簡單的同步、相互排斥操作。
明確這個原理。就能理解為什麼synchronized(this)與synchronized(static XXX)的差別了。synchronized就是針對記憶體區塊申請記憶體鎖,thiskeyword代表類的一個對象,所以其記憶體鎖是針對同樣對象的相互排斥操作,而static成員屬於類專有,其記憶體空間為該類全部成員共同擁有。這就導致synchronized()對static成員加鎖,相當於對類加鎖,也就是在該類的全部成員間實現相互排斥。在同一時間僅僅有一個線程可訪問該類的執行個體。
假設僅僅是簡單的想要實如今JAVA中的線程相互排斥,明確這些基本就已經夠了。
可是假設須要線上程間進行協作通訊,就須要藉助Object對象的wait,notify和notifyAll方法了。
在Java中。能夠通過配合調用Object對象的wait方法和notify方法或notifyAll方法來實現線程間的協作通訊。線上程中調用wait方法。將堵塞等待其它線程的通知(其它線程調用notify方法或notifyAll方法),線上程中調用notify方法或notifyAll方法,將通知其它線程從wait方法處返回。 Object.wait()與Object.notify()必須要與同步塊或同步方法(synchronized塊或者synchronized方法)一起使用,也就是wait與notify是針對已經擷取了Object鎖進行操作,從文法角度來說就是Object.wait(),Object.notify必須在同步塊或同步方法內。從功能上來說wait就是說線程在擷取對象鎖後。主動釋放對象鎖。同一時候本線程休眠。直到有其它線程調用對象的notify()喚醒該線程,才幹繼續擷取對象鎖。並繼續運行。
對應的notify()就是對對象鎖的喚醒操作。
但有一點須要注意的是notify()調用後,並非立即就釋放對象鎖的,而是在對應的同步塊或同步方法中運行結束,自己主動釋放鎖後。JVM會在wait()對象鎖的線程中隨機選取一線程,賦予其對象鎖。喚醒線程,繼續運行。這樣就提供了線上程間同步、喚醒的操作。Thread.sleep()與Object.wait()二者都能夠暫停當前線程,釋放CPU控制權,基本的差別在於Object.wait()在釋放CPU同一時候。釋放了對象鎖的控制。
wait方法
調用線程的sleep,yield方法時,線程並不會讓出對象鎖,wait卻不同。
wait函數必須在同步代碼塊中調用(也就是當前線程必須持有對象的鎖),他的功能是這種:
我累了。歇息一會兒。對象的鎖你們拿去用吧。CPU也給你們。
調用了wait函數的線程會一直等待,直到有其它線程調用了同一個對象的notify或者notifyAll方法才幹被喚醒,須要注意的是:被喚醒並不代表立即獲得對象的鎖。也就是說,一個線程調用了對象的wait方法後,他須要等待兩件事情的發生:
1. 有其它線程調用同一個對象的notify或者notifyAll方法(調用notify/notifyAll方法之前) 2. 被喚醒之後又一次獲得對象的鎖(調用notify/notifyAll方法之後)才幹繼續往下運行興許動作。
假設一個線程調用了某個對象的wait方法,可是興許並沒有其它線程調用該對象的notify或者notifyAll方法。則該線程將會永遠等下去…
notify和notifyAll方法
notify/notifyAll方法也必須在同步代碼塊中調用(也就是調用線程必須持有對象的鎖),他們的功能是這種:
女士們。先生們請注意,鎖的對象我即將用完,請大家醒醒,準備一下。立即你們就能使用鎖了。
不同的是。notify方法僅僅會喚醒一個正在等待的線程(至於喚醒誰,不確定!)。而notifyAll方法會喚醒全部正在等待的線程。
另一點須要特彆強調:調用notify和notifyAll方法後,當前線程並不會立即放棄鎖的持有權,而必須要等待當前同步代碼塊運行完才會讓出鎖。
假設一個對象之前沒有調用wait方法,那麼調用notify方法是沒有不論什麼影響的。
以下來看一個範例:
package com.cooperation;import java.util.concurrent.TimeUnit;public class Test{ public static Object object = new Object(); static class Thread1 implements Runnable { @Override public void run() { synchronized(object) { System.out.println(Thread.currentThread().getName()+" is running."); try { object.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+" get the lock."); } } } static class Thread2 implements Runnable { @Override public void run() { synchronized(object) { System.out.println(Thread.currentThread().getName()+" is running."); object.notify(); System.out.println(Thread.currentThread().getName()+" invoke notify()"); System.out.println(Thread.currentThread().getName()+" release the lock."); } } } public static void main(String[] args) throws InterruptedException { Thread thread1 = new Thread(new Thread1()); Thread thread2 = new Thread(new Thread2()); thread1.start(); TimeUnit.SECONDS.sleep(1); thread2.start(); }} 運行結果(運行多次,結果同樣):
Thread-0 is running.Thread-1 is running.Thread-1 invoke notify()Thread-1 release the lock.Thread-0 get the lock.
能夠看到當Thread-0調用了wait方法後就釋放了object鎖。Thread-1擷取鎖之後調用notify的釋放鎖,可是這個時候Thread沒有立馬擷取object鎖。而是等到了Thread-1的同步塊退出之後才擷取了object的鎖。
假設將class Thread2中的 System.out.println(Thread.currentThread().getName()+" release the lock.");這句放在synchronized(object)的外面。有可能出現例如以下的運行結果:
Thread-0 is running.Thread-1 is running.Thread-1 invoke notify()Thread-0 get the lock.Thread-1 release the lock.
這是由於,當Thread-1釋放了object鎖而且退出了同步塊之後Thread-0立馬擷取了鎖,而這時候兩個線程的列印語句的順序就隨機了。
在JDK1.4之後出現了一個Condition類,這個類也能夠實現同樣的功能。而且一般建議使用Condition替代wait,notify,notifyAll家族,實現更安全的線程間協作通訊功能,比方ArrayBlockingQueue就是使用Condition實現堵塞隊列的。
這個我在以後的部落格中會涉及到。
假設有什麼意見或者建議請在下方留言。
JAVA線程間協作:wait.notify.notifyAll