线程同步以及yield()、wait()、Notify()、Notifyall()
一、線程同步
1、線程同步的目的是為了保護多個線程訪問一個資源時對資源的破壞。 2、線程同步方法是通過鎖來實現(xiàn),每個對象都有切僅有一個鎖,這個鎖與一個特定的對象關(guān)聯(lián),線程一旦獲取了對象鎖,其他訪問該對象的線程就無法再訪問該對象的其他同步方法。 二、實現(xiàn)同步機制的兩個方法 1。同步代碼塊:?synchronized(同一個數(shù)據(jù)){} 同一個數(shù)據(jù):就是N條線程同時訪問一個數(shù)據(jù)。?
2。?
同步方法:?
public synchronized 數(shù)據(jù)返回類型 方法名(){}?
就是使用 synchronized 來修飾某個方法,則該方法稱為同步方法。對于同步方法而言,無需顯示指定同步監(jiān)視器,同步方法的同步監(jiān)視器是 this 也就是該對象的本身(這里指的對象本身有點含糊,其實就是調(diào)用該同步方法的對象)通過使用同步方法,可非常方便的將某類變成線程安全的類,具有如下特征:?
1,該類的對象可以被多個線程安全的訪問。?
2,每個線程調(diào)用該對象的任意方法之后,都將得到正確的結(jié)果。?
3,每個線程調(diào)用該對象的任意方法之后,該對象狀態(tài)依然保持合理狀態(tài)。?
注:synchronized關(guān)鍵字可以修飾方法,也可以修飾代碼塊,但不能修飾構(gòu)造器,屬性等。?
實現(xiàn)同步機制注意以下幾點: 安全性高,性能低,在多線程用。性能高,安全性低,在單線程用。? 關(guān)鍵代碼如下: 編寫打印機類:Printer ?定義兩個方法 package cn.d.happy;public class Printer {Object o=new Object();//或在void前加synchronizedpublic void print1(){//同步代碼塊synchronized (o){System.out.print("線");System.out.print("程");System.out.print("同");System.out.print("步");System.out.println();}}public void print2(){synchronized (o){System.out.print("噢");System.out.print("呵");System.out.println();}} }
定義兩個線程類 并重寫run方法。繼承Thread 和 實現(xiàn)Runnable接口 通過for循環(huán)遍歷次數(shù)
package cn.d.happy;public class MyThread extends Thread{public Printer print;@Override public void run() {//必須有該類的對象實例for (int i = 1; i <=10; i++) {print.print1();} } } package cn.d.happy;public class MyThread2 implements Runnable{public Printer print;@Overridepublic void run() {for (int i = 1; i <=10; i++) {print.print2();}}}測試類 創(chuàng)建打印機對象 ?以及兩個線程對象并進行賦值
package cn.d.happy;public class Test { public static void main(String[] args) {//購買一個打印機Printer p=new Printer();//創(chuàng)建第一個線程對象 并且給屬性賦值MyThread t1=new MyThread();t1.print=p;t1.start();//03.創(chuàng)建第二個線程對象 并且給屬性賦值MyThread2 t2=new MyThread2();t2.print=p;Thread tt=new Thread(t2);tt.start(); } }實現(xiàn)效果:
?
三、Java多線程之yield()、wait()、Notify()、Notifyall()
yield()、
1)????通過yield?()函數(shù),可使線程進入可執(zhí)行狀態(tài),排程器從可執(zhí)行狀態(tài)的線程中重新進行排程。所以調(diào)用了yield()的函數(shù)也有可能馬上被執(zhí)行。
2)????當(dāng)調(diào)用yield?()函數(shù)后,線程不會釋放它的“鎖標志”。
運行結(jié)果為:
t1?:?0
t1?:?1
t1?:?2
t1?:?3
t1?:?0
t1?:?1
t1?:?2
t1?:?3
從結(jié)果可知調(diào)用yield()時并不會釋放對象的“鎖標志”。
如果把代碼(1)注釋掉,并去掉代碼(2)的注釋,結(jié)果為:
t1?:?0
t1?:?1
t2?:?0
t1?:?2
t2?:?1
t1?:?3
t2?:?2
t2?:?3
從結(jié)果可知,雖然t1線程調(diào)用了yield(),但它馬上又被執(zhí)行了。?
?wait與notify是java同步機制中重要的組成部分。結(jié)合與synchronized關(guān)鍵字使用,可以建立很多優(yōu)秀的同步模型。
?synchronized(this){ }等價于publicsynchronized void method(){.....}
?同步分為類級別和對象級別,分別對應(yīng)著類鎖和對象鎖。類鎖是每個類只有一個,如果static的方法被synchronized關(guān)鍵字修飾,則在這個方法被執(zhí)行前必須獲得類鎖;對象鎖類同。
???首先,調(diào)用一個Object的wait與notify/notifyAll的時候,必須保證調(diào)用代碼對該Object是同步的,也就是說必須在作用等同于synchronized(obj){......}的內(nèi)部才能夠去調(diào)用obj的wait與notify/notifyAll三個方法,否則就會報錯:
??java.lang.IllegalMonitorStateException:current thread not owner
??在調(diào)用wait的時候,線程自動釋放其占有的對象鎖,同時不會去申請對象鎖。當(dāng)線程被喚醒的時候,它才再次獲得了去獲得對象鎖的權(quán)利。
??所以,notify與notifyAll沒有太多的區(qū)別,只是notify僅喚醒一個線程并允許它去獲得鎖,notifyAll是喚醒所有等待這個對象的線程并允許它們?nèi)カ@得對象鎖,只要是在synchronied塊中的代碼,沒有對象鎖是寸步難行的。其實喚醒一個線程就是重新允許這個線程去獲得對象鎖并向下運行。
???notifyAll,雖然是對每個wait的對象都調(diào)用一次notify,但是這個還是有順序的,每個對象都保存這一個等待對象鏈,調(diào)用的順序就是這個鏈的順序。其實啟動等待對象鏈中各個線程的也是一個線程,在具體應(yīng)用的時候,需要注意一下。
??wait(),notify(),notifyAll()不屬于Thread類,而是屬于Object基礎(chǔ)類,也就是說每個對像都有wait(),notify(),notifyAll()的功能。因為都個對像都有鎖,鎖是每個對像的基礎(chǔ),當(dāng)然操作鎖的方法也是最基礎(chǔ)了。
wait():
等待對象的同步鎖,需要獲得該對象的同步鎖才可以調(diào)用這個方法,否則編譯可以通過,但運行時會收到一個異常:IllegalMonitorStateException。
調(diào)用任意對象的 wait() 方法導(dǎo)致該線程阻塞,該線程不可繼續(xù)執(zhí)行,并且該對象上的鎖被釋放。
notify():
喚醒在等待該對象同步鎖的線程(只喚醒一個,如果有多個在等待),注意的是在調(diào)用此方法的時候,并不能確切的喚醒某一個等待狀態(tài)的線程,而是由JVM確定喚醒哪個線程,而且不是按優(yōu)先級。
調(diào)用任意對象的notify()方法則導(dǎo)致因調(diào)用該對象的 wait()方法而阻塞的線程中隨機選擇的一個解除阻塞(但要等到獲得鎖后才真正可執(zhí)行)。
notifyAll():
喚醒所有等待的線程,注意喚醒的是notify之前wait的線程,對于notify之后的wait線程是沒有效果的。
?
通常,多線程之間需要協(xié)調(diào)工作:如果條件不滿足,則等待;當(dāng)條件滿足時,等待該條件的線程將被喚醒。在Java中,這個機制的實現(xiàn)依賴于wait/notify。等待機制與鎖機制是密切關(guān)聯(lián)的。
例如:
?
synchronized(obj) {while(!condition) {obj.wait();}obj.doSomething();}?
當(dāng)線程A獲得了obj鎖后,發(fā)現(xiàn)條件condition不滿足,無法繼續(xù)下一處理,于是線程A就wait()。
在另一線程B中,如果B更改了某些條件,使得線程A的condition條件滿足了,就可以喚醒線程A :
synchronized和wait()、notify()等的關(guān)系:?
1.有synchronized的地方不一定有wait,notify
2.有wait,notify的地方必有synchronized.這是因為wait和notify不是屬于線程類,而是每一個對象都具有的方法,而且,這兩個方法都和對象鎖有關(guān),有鎖的地方,必有synchronized。
另外,注意一點:如果要把notify和wait方法放在一起用的話,必須先調(diào)用notify后調(diào)用wait,因為如果調(diào)用完wait,該線程就已經(jīng)不是currentthread了。
?
?
?
?
?
轉(zhuǎn)載于:https://www.cnblogs.com/WJ-163/p/5770304.html
總結(jié)
以上是生活随笔為你收集整理的线程同步以及yield()、wait()、Notify()、Notifyall()的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: spring 多线程 事务 源码解析(一
- 下一篇: Error: Cannot find m