簡介
java中的同步是控製多線程對任何共享資源的訪問的能力。在多線程概念中,多個線程試圖同時訪問共享資源,從而產生不一致的結果。同步對於線程之間的可靠通信是必要的。
為什麼要使用同步
- 同步有助於防止線程幹擾。
- 同步有助於防止並發問題。
類型的同步
同步分為兩種類型
- 進程同步
- 線程同步
進程同步:
- 這個過程隻是一個正在執行的程序。它獨立運行,與另一個進程隔離。內存、CPU時間等資源由操作係統分配給進程。
線程同步:
線程同步有兩種類型,它們是:
1.相互排斥的:
互斥互斥隻能幫助一個線程訪問共享資源。它不允許一次訪問共享資源。它可以通過以下方式實現。
- 同步的方法
- 同步塊
- 靜態同步
2.協作(java中的線程間通信)
Java中的鎖概念
- 利用java語言中synchronized關鍵字開發的同步機製。它構建在鎖定機製之上,該鎖定機製由Java虛擬機(JVM)負責。synchronized關鍵字隻適用於方法和塊,不能應用於類和變量。同步關鍵字在java中創建的代碼塊被稱為臨界段。為了進入臨界區,線程需要獲得相應對象的鎖。
沒有同步的問題:
下麵的例子展示了像n這樣的數字的冪1n2n3.n4n5
class Power{void printPower(int n){//方法不同步int temp = 1;(int i = 1;我< = 5;+ +){System.out.println (Thread.currentThread . getname () () + ":- " + n +“^”+我+ + n *臨時“價值:”);temp = n *溫度;嚐試{thread . sleep (500);}catch(Exception e){System.out.println(e);}}}}類Thread1擴展線程{Power p;Thread1(功率p) {this.p = p;}公共無效運行(){p.printPower(5);}}類Thread2擴展了線程{Power p;Thread2(功率p) {this.p = p;}公共無效運行(){p.printPower(8); } } public class Synchronization_Example1{ public static void main(String args[]){ Power obj = new Power();//only one object Thread1 p1=new Thread1(obj); Thread2 p2=new Thread2(obj); p1.start(); p2.start(); } }
輸出:
Thread-1:- 8^1值:8
線程0:- 5^1值:5
Thread-1:—8^2值:64
線程0:- 5^2值:25
Thread-1:- 8^3值:512
線程0:- 5^3值:125
線程-1:- 8^4值:4096
線程0:- 5^4值:625
線程1:- 8^5值:32768
線程0:- 5^5值:3125
這裏我們沒有使用synchronized關鍵字,所以兩個線程同時執行,所以在輸出中,線程0幹擾線程1,因此,我們得到的結果不一致。
Java同步方法
如果我們在任何方法中使用Synchronized關鍵字,那麼該方法就是Synchronized方法。
- 它用於鎖定任何共享資源的對象。
- 對象在調用synchronized方法時獲得鎖。
- 直到線程完成它的功能,鎖才會被釋放。
語法:
同步的return_type method_name (Method_Parameters) {
//方法的代碼。
}
Java同步方法示例:
class Power{synchronized void printPower(int n){//方法synchronized int temp = 1;(int i = 1;我< = 5;+ +){System.out.println (Thread.currentThread . getname () () + ":- " + n +“^”+我+ + n *臨時“價值:”);temp = n *溫度;嚐試{thread . sleep (500);}catch(Exception e){System.out.println(e);}}}}類Thread1擴展線程{Power p;Thread1(功率p) {this.p = p;}公共無效運行(){p.printPower(5);}}類Thread2擴展了線程{Power p;Thread2(功率p) {this.p = p;}公共無效運行(){p.printPower(8); } } public class Synchronization_Example2{ public static void main(String args[]){ Power obj = new Power();//only one object Thread1 p1=new Thread1(obj); Thread2 p2=new Thread2(obj); p1.start(); p2.start(); } }
輸出:
線程0:- 5^1值:5
線程0:- 5^2值:25
線程0:- 5^3值:125
線程0:- 5^4值:625
線程0:- 5^5值:3125
Thread-1:- 8^1值:8
Thread-1:—8^2值:64
Thread-1:- 8^3值:512
線程-1:- 8^4值:4096
線程1:- 8^5值:32768
這裏我們使用同步關鍵字。它有助於一次執行一個線程。在第一個線程完成之前,它不允許另一個線程執行,在第一個線程完成後,它允許第二個線程執行。現在我們可以正確地看到輸出n的5和8次方1到n5.線程0完成後,隻有線程1開始。
同步塊
- 假設您不想同步整個方法,而是想同步方法中的幾行代碼,那麼同步塊將幫助同步這幾行代碼。它將對象作為參數。它的工作原理與同步方法相同。在同步方法的情況下,被訪問的鎖在方法上,而在同步塊的情況下,被訪問的鎖在對象上。
語法:
Synchronized (object){//塊的代碼。理解Synchronized Block的程序:類Power{void printPower(int n){Synchronized (this){// Synchronized Block int temp = 1;(int i = 1;我< = 5;+ +){System.out.println (Thread.currentThread . getname () () + ":- " + n +“^”+我+ + n *臨時“價值:”);temp = n *溫度;嚐試{thread . sleep (500);}catch(Exception e){System.out.println(e);}}}}}類Thread1擴展線程{Power p;Thread1(功率p) {this.p = p;}公共無效運行(){p.printPower(5);}}類Thread2擴展了線程{Power p;Thread2(功率p) {this.p = p; } public void run(){ p.printPower(8); } } public class Synchronization_Example3{ public static void main(String args[]){ Power obj = new Power();//only one object Thread1 p1=new Thread1(obj); Thread2 p2=new Thread2(obj); p1.start(); p2.start(); } }
輸出:
線程0:- 5^1值:5
線程0:- 5^2值:25
線程0:- 5^3值:125
線程0:- 5^4值:625
線程0:- 5^5值:3125
Thread-1:- 8^1值:8
Thread-1:—8^2值:64
Thread-1:- 8^3值:512
線程-1:- 8^4值:4096
線程1:- 8^5值:32768
在這個例子中,我們沒有同步整個方法,但是我們同步了方法中的幾行代碼。我們得到的結果和同步方法完全一樣。
靜態同步
- 在java中,每個對象都有一個與之關聯的鎖(監視器)。進入synchronized方法或synchronized塊的線程將獲得鎖,其他所有剩餘的使用共享資源的線程必須等待第一個線程完成並釋放鎖。
- 假設在有多個對象的情況下,在這種情況下,兩個獨立的線程將獲得鎖,並進入同步塊或同步方法,同時為每個對象提供單獨的鎖。為了避免這種情況,我們將使用靜態同步。
- 在這裏,我們將把同步關鍵字放在靜態方法之前。在靜態同步中,鎖訪問是在類上,而不是在對象和方法上。
語法:
synchronized static return_type method_name (Parameters) {//code}或synchronized static return_type method_name (Class_name.class) {//code}沒有靜態同步的程序:class Power{synchronized void printPower(int n){//static synchronized method int temp = 1;(int i = 1;我< = 5;+ +){System.out.println (Thread.currentThread . getname () () + ":- " + n +“^”+我+ + n *臨時“價值:”);temp = n *溫度;嚐試{thread . sleep (400);}catch(異常e){}}}}類Thread1擴展線程{功率p;Thread1(功率p) {this.p = p;}公共無效運行(){p.printPower(2);}}類Thread2擴展了線程{Power p;Thread2(功率p) {this.p = p;}公共無效運行(){p.printPower(3); } } class Thread3 extends Thread{ Power p; Thread3(Power p){ this.p=p; } public void run(){ p.printPower(5); } } class Thread4 extends Thread{ Power p; Thread4(Power p){ this.p=p; } public void run(){ p.printPower(8); } } public class Synchronization_Example4{ public static void main(String args[]){ Power ob1 = new Power(); //first object Power ob2 = new Power(); //second object Thread1 p1 = new Thread1(ob1); Thread2 p2 = new Thread2(ob1); Thread3 p3 = new Thread3(ob2); Thread4 p4 = new Thread4(ob2); p1.start(); p2.start(); p3.start(); p4.start(); } }
輸出:
線程2:- 5^1值:5
線程0:- 2^1值:2
線程2:- 5^2值:25
線程0:- 2^2值:4
線程2:- 5^3值:125
線程0:- 2^3值:8
線程2:- 5^4值:625
線程0:- 2^4值:16
線程2:- 5^5值:3125
線程0:- 2^5值:32
線程3:- 8^1值:8
Thread-1:- 3^1值:3
線程3:- 8^2值:64
Thread-1:- 3^2值:9
線程3:- 8^3值:512
Thread-1:- 3^3值:27
線程3:- 8^4值:4096
線程-1:—3^4值:81
線程3:- 8^5值:32768
Thread-1:—3^5值:243
如果你觀察上麵的結果,Thread-0, Thread-1屬於object-1, Thread-2, Thread-3屬於Object-2。因此,由於相同的對象(obj1),線程0和線程1之間沒有幹擾。以同樣的方式,線程2和線程3之間沒有幹擾,因為它們屬於同一個對象(obj2)。但是如果您觀察到線程0和2之間有幹擾,就像線程1和3之間有幹擾一樣。為了糾正這個問題,我們將使用靜態同步。
靜態同步程序:
class Power{synchronized static void printPower(int n){//靜態同步方法int temp = 1;(int i = 1;我< = 5;+ +){System.out.println (Thread.currentThread . getname () () + ":- " + n +“^”+我+ + n *臨時“價值:”);temp = n *溫度;嚐試{thread . sleep (400);}catch(異常e){}}}}類Thread1擴展線程{功率p;Thread1(功率p) {this.p = p;}公共無效運行(){p.printPower(2);}}類Thread2擴展了線程{Power p;Thread2(功率p) {this.p = p;}公共無效運行(){p.printPower(3); } } class Thread3 extends Thread{ Power p; Thread3(Power p){ this.p=p; } public void run(){ p.printPower(5); } } class Thread4 extends Thread{ Power p; Thread4(Power p){ this.p=p; } public void run(){ p.printPower(8); } } public class Synchronization_Example4{ public static void main(String args[]){ Power ob1 = new Power(); //first object Power ob2 = new Power(); //second object Thread1 p1 = new Thread1(ob1); Thread2 p2 = new Thread2(ob1); Thread3 p3 = new Thread3(ob2); Thread4 p4 = new Thread4(ob2); p1.start(); p2.start(); p3.start(); p4.start(); } }
輸出:
線程0:- 2^1值:2
線程0:- 2^2值:4
線程0:- 2^3值:8
線程0:- 2^4值:16
線程0:- 2^5值:32
Thread-1:- 3^1值:3
Thread-1:- 3^2值:9
Thread-1:- 3^3值:27
線程-1:—3^4值:81
Thread-1:—3^5值:243
線程2:- 5^1值:5
線程2:- 5^2值:25
線程2:- 5^3值:125
線程2:- 5^4值:625
線程2:- 5^5值:3125
線程3:- 8^1值:8
線程3:- 8^2值:64
線程3:- 8^3值:512
線程3:- 8^4值:4096
線程3:- 8^5值:32768
在這個靜態同步中,我們可以觀察到Thread-0和Thread-2之間沒有幹擾,就像Thread-1和thread - 3之間沒有幹擾一樣。下一個線程隻在前一個線程完成或釋放鎖之後執行。
線程間通信
線程間通信或合作是兩個或多個線程之間的通信。這可以通過以下方法來實現。
- wait ()
- notify ()
- notifyAll ()
為什麼我們需要線程間通信?
- 線程上有這樣一種情況,它不斷重複檢查某些條件,一旦條件滿足,線程就會采取適當的行動。這種情況被稱為輪詢。這是對CPU時間的浪費,為了減少由於輪詢造成的CPU時間的浪費,java使用了線程間通信機製。
- wait(), notify(), notifyAll()方法必須在同步方法或塊中調用,否則程序將編譯,但當您運行它時,它將拋出非法的監視器狀態異常。
例子:
class Power{void printPower(int n){int temp = 1;(int i = 1;我< = 5;+ +){System.out.println (Thread.currentThread . getname () () + ":- " + n +“^”+我+ + n *臨時“價值:”);temp = n *溫度;嚐試{this.wait ();//等待放在同步塊或方法的外麵;}catch(異常e){System.out.println(e);}}}}
輸出:
線程0:- 5^1值:5
java.lang.IllegalMonitorStateException
線程0:- 5^2值:25
java.lang.IllegalMonitorStateException
線程0:- 5^3值:125
java.lang.IllegalMonitorStateException
線程0:- 5^4值:625
java.lang.IllegalMonitorStateException
線程0:- 5^5值:3125
java.lang.IllegalMonitorStateException
Thread-1:- 8^1值:8
java.lang.IllegalMonitorStateException
Thread-1:—8^2值:64
java.lang.IllegalMonitorStateException
Thread-1:- 8^3值:512
java.lang.IllegalMonitorStateException
線程-1:- 8^4值:4096
java.lang.IllegalMonitorStateException
線程1:- 8^5值:32768
java.lang.IllegalMonitorStateException
- wait()方法
- 它導致當前線程將自己置於等待階段,直到另一個線程調用該對象的notify()方法或notifyAll()方法。
- notify()方法
- 該方法喚醒同一對象上名為wait()的單個線程。如果有多個線程在等待同一個對象,那麼任意選擇其中的一個線程被喚醒。在這裏,被喚醒的線程直到當前線程釋放鎖才能夠繼續。如果任何線程試圖獲得這個對象上的鎖,那麼被喚醒的線程也將以通常的方式與它們競爭。
語法:
公共最終無效notify()
- 通知所有()方法
- 它將喚醒在此對象監視器上等待的所有線程,而不是單個線程。被喚醒的線程將無法繼續,直到當前線程釋放鎖。同樣,這些被喚醒的線程需要與所有試圖獲得該對象上的鎖的其他線程競爭。
語法:
notifyAll()
同步機製的缺點
同步機製顯示更少的性能。
讓我們考慮一個例子,如果有五個進程P1, P2, P3, P4, P5,它們都在等待共享資源一次隻能訪問一個線程,那麼所有其他進程都處於等待狀態,最後一個進程必須等待所有其他進程完成。因此,我們必須使用同步的概念,我們將得到不一致的結果。
如果您有興趣了解更多關於Java的知識,請訪問beplay2018官网很好的學習學院.