Java Synchronized的使用詳解
1.為什麼要使用synchronized
在併發程式設計中存線上程安全問題,主要原因有:1.存在共享資料 2.多執行緒共同操作共享資料。關鍵字synchronized可以保證在同一時刻,只有一個執行緒可以執行某個方法或某個程式碼塊,同時synchronized可以保證一個執行緒的變化可見(可見性),即可以代替volatile。
2.實現原理
synchronized可以保證方法或者程式碼塊在執行時,同一時刻只有一個方法可以進入到臨界區,同時它還可以保證共享變數的記憶體可見性
3.synchronized的三種應用方式
Java中每一個物件都可以作為鎖,這是synchronized實現同步的基礎:
普通同步方法(例項方法),鎖是當前例項物件 ,進入同步程式碼前要獲得當前例項的鎖靜態同步方法,鎖是當前類的class物件 ,進入同步程式碼前要獲得當前類物件的鎖同步方法塊,鎖是括號裡面的物件,對給定物件加鎖,進入同步程式碼庫前要獲得給定物件的鎖。
4.synchronized的作用
Synchronized是Java中解決併發問題的一種最常用最簡單的方法 ,他可以確保執行緒互斥的訪問同步程式碼
5.舉栗子
**一、synchronized作用於例項方法**
①多個執行緒訪問同一個物件的同一個方法
public class synchronizedTest implements Runnable { //共享資源 static int i =0; /** * synchronized 修飾例項方法 */ public synchronized void increase(){ i++; } @Override public void run(){ for (int j =0 ; j<10000;j++){ increase(); } } public static void main(String[] args) throws InterruptedException { synchronizedTest test = new synchronizedTest(); Thread t1 = new Thread(test); Thread t2 = new Thread(test); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println(i); } }
結果:
分析:當兩個執行緒同時對一個物件的一個方法進行操作,只有一個執行緒能夠搶到鎖。因為一個物件只有一把鎖,一個執行緒獲取了該物件的鎖之後,其他執行緒無法獲取該物件的鎖,就不能訪問該物件的其他synchronized例項方法,但是可以訪問非synchronized修飾的方法
②一個執行緒獲取了該物件的鎖之後,其他執行緒來訪問其他synchronized例項方法現象 舉慄
public class SynchronizedTest { public synchronized void method1() { System.out.println("Method 1 start"); try { System.out.println("Method 1 execute"); Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Method 1 end"); } public synchronized void method2() { System.out.println("Method 2 start"); try { System.out.println("Method 2 execute"); Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Method 2 end"); } public static void main(String[] args) { final SynchronizedTest test = new SynchronizedTest(); new Thread(test::method1).start(); new Thread(test::method2).start(); } }
結果:
分析:可以看出其他執行緒來訪問synchronized修飾的其他方法時需要等待執行緒1先把鎖釋放
③一個執行緒獲取了該物件的鎖之後,其他執行緒來訪問其他非synchronized例項方法現象 舉慄
去掉②中方法二的synchronized
public class SynchronizedTest { public synchronized void method1() { System.out.println("Method 1 start"); try { System.out.println("Method 1 execute"); Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Method 1 end"); } public void method2() { System.out.println("Method 2 start"); try { System.out.println("Method 2 execute"); Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Method 2 end"); } public static void main(String[] args) { final SynchronizedTest test = new SynchronizedTest(); new Thread(test::method1).start(); new Thread(test::method2).start(); } }
結果:
分析:當執行緒1還在執行時,執行緒2也執行了,所以當其他執行緒來訪問非synchronized修飾的方法時是可以訪問的
④當多個執行緒作用於不同的物件
public class SynchronizedTest { public synchronized void method1() { System.out.println("Method 1 start"); try { System.out.println("Method 1 execute"); Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Method 1 end"); } public synchronized void method2() { System.out.println("Method 2 start"); try { System.out.println("Method 2 execute"); Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Method 2 end"); } public static void main(String[] args) { final SynchronizedTest test1 = new SynchronizedTest(); final SynchronizedTest test2 = new SynchronizedTest(); new Thread(test1::method1).start(); new Thread(test2::method2).start(); } }
結果:
分析:因為兩個執行緒作用於不同的物件,獲得的是不同的鎖,所以互相併不影響
**此處思考一個問題:為什麼分散式環境下synchronized失效?如何解決這種情況?
****二、synchronized作用於靜態方法**
public class synchronizedTest implements Runnable { //共享資源 static int i =0; /** * synchronized 修飾例項方法 */ public static synchronized void increase(){ i++; } @Override public void run(){ for (int j =0 ; j<10000;j++){ increase(); } } public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(new synchronizedTest()); Thread t2 = new Thread(new synchronizedTest()); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println(i); }
結果:
分析:由例子可知,兩個執行緒例項化兩個不同的物件,但是訪問的方法是靜態的,兩個執行緒發生了互斥(即一個執行緒訪問,另一個執行緒只能等著),因為靜態方法是依附於類而不是物件的,當synchronized修飾靜態方法時,鎖是class物件。
**三、synchronized作用於同步程式碼塊**
為什麼要同步程式碼塊呢?在某些情況下,我們編寫的方法體可能比較大,同時存在一些比較耗時的操作,而需要同步的程式碼又只有一小部分,如果直接對整個方法進行同步操作,可能會得不償失,此時我們可以使用同步程式碼塊的方式對需要同步的程式碼進行包裹,這樣就無需對整個方法進行同步操作了。
public class synchronizedTest implements Runnable { static synchronizedTest instance=new synchronizedTest(); static int i=0; @Override public void run() { //省略其他耗時操作.... //使用同步程式碼塊對變數i進行同步操作,鎖物件為instance synchronized(instance){ for(int j=0;j<10000;j++){ i++; } } } public static void main(String[] args) throws InterruptedException { Thread t1=new Thread(instance); Thread t2=new Thread(instance); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println(i); } }
結果:
分析:將synchronized作用於一個給定的例項物件instance,即當前例項物件就是鎖物件,每次當執行緒進入synchronized包裹的程式碼塊時就會要求當前執行緒持有instance例項物件鎖,如果當前有其他執行緒正持有該物件鎖,那麼新到的執行緒就必須等待,這樣也就保證了每次只有一個執行緒執行i++;操作。當然除了instance作為物件外,我們還可以使用this物件(代表當前例項)或者當前類的class物件作為鎖,如下程式碼:
//this,當前例項物件鎖 synchronized(this){ for(int j=0;j<1000000;j++){ i++; } } //class物件鎖 synchronized(AccountingSync.class){ for(int j=0;j<1000000;j++){ i++; } }
下一篇將深入介紹Synchronized的實現原理
到此這篇關於Java Synchronized的使用詳解的文章就介紹到這了,更多相關Java Synchronized使用內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!