一、線程同步
synchronized(同一個數據){} 同一個數據:就是N條線程同時訪問一個數據。?
2。?
同步方法:?
public synchronized 數據返回類型 方法名(){}?
就是使用 synchronized 來修飾某個方法,則該方法稱為同步方法。對于同步方法而言,無需顯示指定同步監視器,同步方法的同步監視器是 this 也就是該對象的本身(這里指的對象本身有點含糊,其實就是調用該同步方法的對象)通過使用同步方法,可非常方便的將某類變成線程安全的類,具有如下特征:?
1,該類的對象可以被多個線程安全的訪問。?
2,每個線程調用該對象的任意方法之后,都將得到正確的結果。?
3,每個線程調用該對象的任意方法之后,該對象狀態依然保持合理狀態。?
注:synchronized關鍵字可以修飾方法,也可以修飾代碼塊,但不能修飾構造器,屬性等。?
實現同步機制注意以下幾點: 安全性高,性能低,在多線程用。性能高,安全性低,在單線程用。?
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 和 實現Runnable接口 通過for循環遍歷次數
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();}}}
測試類 創建打印機對象 ?以及兩個線程對象并進行賦值
package cn.d.happy;public class Test { public static void main(String[] args) {//購買一個打印機Printer p=new Printer();//創建第一個線程對象 并且給屬性賦值MyThread t1=new MyThread();t1.print=p;t1.start();//03.創建第二個線程對象 并且給屬性賦值MyThread2 t2=new MyThread2();t2.print=p;Thread tt=new Thread(t2);tt.start(); } }
實現效果:
?
三、Java多線程之yield()、wait()、Notify()、Notifyall()
yield()、
1)????通過yield?()函數,可使線程進入可執行狀態,排程器從可執行狀態的線程中重新進行排程。所以調用了yield()的函數也有可能馬上被執行。
2)????當調用yield?()函數后,線程不會釋放它的“鎖標志”。
class TestThreadMethod extends Thread{public static int shareVar = 0;public TestThreadMethod(String name){super(name);}public synchronized void run(){for(int i=0; i<4; i++){System.out.print(Thread.currentThread().getName());System.out.println(" : " + i);Thread.yield();}}}public class TestThread{public static void main(String[] args){TestThreadMethod t1 = new TestThreadMethod("t1");TestThreadMethod t2 = new TestThreadMethod("t2");t1.start();t1.start(); //(1)//t2.start(); (2)}
運行結果為:
t1?:?0
t1?:?1
t1?:?2
t1?:?3
t1?:?0
t1?:?1
t1?:?2
t1?:?3
從結果可知調用yield()時并不會釋放對象的“鎖標志”。
如果把代碼(1)注釋掉,并去掉代碼(2)的注釋,結果為:
t1?:?0
t1?:?1
t2?:?0
t1?:?2
t2?:?1
t1?:?3
t2?:?2
t2?:?3
從結果可知,雖然t1線程調用了yield(),但它馬上又被執行了。?
?wait與notify是java同步機制中重要的組成部分。結合與synchronized關鍵字使用,可以建立很多優秀的同步模型。
?synchronized(this){ }等價于publicsynchronized void method(){.....}
?同步分為類級別和對象級別,分別對應著類鎖和對象鎖。類鎖是每個類只有一個,如果static的方法被synchronized關鍵字修飾,則在這個方法被執行前必須獲得類鎖;對象鎖類同。
???首先,調用一個Object的wait與notify/notifyAll的時候,必須保證調用代碼對該Object是同步的,也就是說必須在作用等同于synchronized(obj){......}的內部才能夠去調用obj的wait與notify/notifyAll三個方法,否則就會報錯:
??java.lang.IllegalMonitorStateException:current thread not owner
??在調用wait的時候,線程自動釋放其占有的對象鎖,同時不會去申請對象鎖。當線程被喚醒的時候,它才再次獲得了去獲得對象鎖的權利。
??所以,notify與notifyAll沒有太多的區別,只是notify僅喚醒一個線程并允許它去獲得鎖,notifyAll是喚醒所有等待這個對象的線程并允許它們去獲得對象鎖,只要是在synchronied塊中的代碼,沒有對象鎖是寸步難行的。其實喚醒一個線程就是重新允許這個線程去獲得對象鎖并向下運行。
???notifyAll,雖然是對每個wait的對象都調用一次notify,但是這個還是有順序的,每個對象都保存這一個等待對象鏈,調用的順序就是這個鏈的順序。其實啟動等待對象鏈中各個線程的也是一個線程,在具體應用的時候,需要注意一下。
??wait(),notify(),notifyAll()不屬于Thread類,而是屬于Object基礎類,也就是說每個對像都有wait(),notify(),notifyAll()的功能。因為都個對像都有鎖,鎖是每個對像的基礎,當然操作鎖的方法也是最基礎了。
wait():
等待對象的同步鎖,需要獲得該對象的同步鎖才可以調用這個方法,否則編譯可以通過,但運行時會收到一個異常:IllegalMonitorStateException。
調用任意對象的 wait() 方法導致該線程阻塞,該線程不可繼續執行,并且該對象上的鎖被釋放。
notify():
喚醒在等待該對象同步鎖的線程(只喚醒一個,如果有多個在等待),注意的是在調用此方法的時候,并不能確切的喚醒某一個等待狀態的線程,而是由JVM確定喚醒哪個線程,而且不是按優先級。
調用任意對象的notify()方法則導致因調用該對象的 wait()方法而阻塞的線程中隨機選擇的一個解除阻塞(但要等到獲得鎖后才真正可執行)。
notifyAll():
喚醒所有等待的線程,注意喚醒的是notify之前wait的線程,對于notify之后的wait線程是沒有效果的。
?
通常,多線程之間需要協調工作:如果條件不滿足,則等待;當條件滿足時,等待該條件的線程將被喚醒。在Java中,這個機制的實現依賴于wait/notify。等待機制與鎖機制是密切關聯的。
例如:
?
synchronized(obj) {while(!condition) {obj.wait();}obj.doSomething();}
?
當線程A獲得了obj鎖后,發現條件condition不滿足,無法繼續下一處理,于是線程A就wait()。
在另一線程B中,如果B更改了某些條件,使得線程A的condition條件滿足了,就可以喚醒線程A :
synchronized(obj) {condition = true;obj.notify();}
synchronized和wait()、notify()等的關系:?
1.有synchronized的地方不一定有wait,notify
2.有wait,notify的地方必有synchronized.這是因為wait和notify不是屬于線程類,而是每一個對象都具有的方法,而且,這兩個方法都和對象鎖有關,有鎖的地方,必有synchronized。
另外,注意一點:如果要把notify和wait方法放在一起用的話,必須先調用notify后調用wait,因為如果調用完wait,該線程就已經不是currentthread了。
?
?
?
?
?