一、線程同步
synchronized(同一個數據){} 同一個數據:就是N條線程同時訪問一個數據。
2。
同步方法:
public synchronized 數據返回類型 方法名(){}
就是使用 synchronized 來修飾某個方法,則該方法稱為同步方法。對于同步方法而言,無需顯示指定同步監視器,同步方法的同步監視器是 this 也就是該對象的本身(這里指的對象本身有點含糊,其實就是調用該同步方法的對象)通過使用同步方法,可非常方便的將某類變成線程安全的類,具有如下特征:
1,該類的對象可以被多個線程安全的訪問。
2,每個線程調用該對象的任意方法之后,都將得到正確的結果。
3,每個線程調用該對象的任意方法之后,該對象狀態依然保持合理狀態。
注:synchronized關鍵字可以修飾方法,也可以修飾代碼塊,但不能修飾構造器,屬性等。
實現同步機制注意以下幾點: 安全性高,性能低,在多線程用。性能高,安全性低,在單線程用。
package cn.d.happy; public class Printer { Object o=new Object(); //或在void前加synchronized public 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; @Override public 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了。
文章列表