您好,登錄后才能下訂單哦!
小編給大家分享一下Java多線程之synchronized同步代碼塊的示例分析,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
同步塊更好,這意味著同步塊之外的代碼是異步執(zhí)行的,這比同步整個方法更提升代碼的效率。請知道一條原則:同步的范圍越小越好。
對于小的臨界區(qū),我們直接在方法聲明中設(shè)置synchronized同步關(guān)鍵字,可以避免競態(tài)條件的問題。但是對于較大的臨界區(qū)代碼段,為了執(zhí)行效率,最好將同步方法分為小的臨界區(qū)代碼段。
public class TwoPlus { private int num1 = 0; private int num2 = 0; public synchronized void plus(int val1,int val2){ this.num1 = num1+val1; this.num2 = num2+val2; } }
臨界區(qū)代碼段包含對兩個臨界區(qū)資源的操作,這兩個臨界區(qū)資源分別為sum1和sum2。使用synchronized對plus(int val1,int val2)進行同步保護之后,進入臨界區(qū)代碼段的線程擁有sum1和sum2的操作權(quán),并且是全部占用。一旦線程進入,當(dāng)線程在操作sum1而沒有操作sum2時,也將sum2的操作權(quán)白白占用,其他的線程由于沒有進入臨界區(qū),只能看著sum2被閑置而不能去執(zhí)行操作。
所以,將synchronized加在方法上,如果其保護的臨界區(qū)代碼段包含的臨界區(qū)資源多于一個,就會造成臨界區(qū)資源的閑置等待,進而會影響臨界區(qū)代碼段的吞吐量。為了提升吞吐量,可以將synchronized關(guān)鍵字放在函數(shù)體內(nèi),同步一個代碼塊。synchronized同步塊的寫法是:
synchronized (syncObject){ // 臨界區(qū)代碼段的代碼塊 }
在synchronized同步塊后邊的括號中是一個syncObject對象,代表著進入臨界區(qū)代碼段需要獲取syncObject對象的監(jiān)視鎖,由于每一個Java對象都有一把監(jiān)視鎖,因此任何Java對象都能作為synchronized的同步鎖。
使用synchronized同步塊對上面的TwoPlus類進行吞吐量的提升改造,具體的代碼如下:
public class TwoPlus { private int num1 = 0; private int num2 = 0; // 兩把不同的鎖對象 private Object object1 = new Object(); private Object object2 = new Object(); public void plus(int val1,int val2){ synchronized (object1){ this.num1 = num1+val1; } synchronized (object2){ this.num2 = num2+val2; } } }
改造之后,對兩個獨立的臨界區(qū)資源sum1和sum2的加法操作可以并發(fā)執(zhí)行了,在某一個時刻,不同的線程可以對sum1和sum2同時進行加法操作,提升了plus()方法的吞吐量。
synchronized
方法和synchronized
同步塊有什么區(qū)別呢?
總體來說,synchronized方法是一種粗粒度的并發(fā)控制,某一時刻只能有一個線程執(zhí)行該synchronized方法;而synchronized代碼塊是一種細(xì)粒度的并發(fā)控制,處于synchronized塊之外的其他代碼是可以被多個線程并發(fā)訪問的。在一個方法中,并不一定所有代碼都是臨界區(qū)代碼段,可能只有幾行代碼會涉及線程同步問題。所以synchronized代碼塊比synchronized方法更加細(xì)粒度地控制了多個線程的同步訪問。
synchronized方法的同步鎖實質(zhì)上使用了this對象鎖,這樣就免去了手工設(shè)置同步鎖的工作。而使用synchronized代碼塊需要手工設(shè)置同步鎖。
public class RoomDemo { private static int count = 0; // 創(chuàng)建鎖對象,同步代碼塊需要手動設(shè)置對象鎖 private static Object object = new Object(); public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(()->{ for(int i=0;i<5000;i++){ // 使用object對象鎖住臨界區(qū)資源 synchronized (object){ count++; } } },"t1"); Thread t2 = new Thread(()->{ // 使用object對象鎖住臨界區(qū)資源 for(int i=0;i<5000;i++){ synchronized (object){ count--; } } },"t2"); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println(count);// 0 } }
你可以做這樣的類比: synchronized(對象) 中的對象,可以想象為一個房間,線程 t1,t2 想象成兩個人
(1) 當(dāng)線程 t1 執(zhí)行到 synchronized(object) 時就好比 t1 進入了這個房間,并鎖住了門拿走了鑰匙,在門內(nèi)執(zhí)行 count++ 代碼 ;
(2) 這時候如果 t2 也運行到了 synchronized(object) 時,它發(fā)現(xiàn)門被鎖住了,只能在門外等待,發(fā)生了上下文切換,阻塞住了 ;
(3) 這中間即使 t1 的 cpu 時間片不幸用完,被踢出了門外 (不要錯誤理解為鎖住了對象就能一直執(zhí)行下去哦) , 這時門還是鎖住的,t1 仍拿著鑰匙,t2 線程還在阻塞狀態(tài)進不來,只有下次輪到 t1 自己再次獲得時間片時才 能開門進入
(4) 當(dāng) t1 執(zhí)行完 synchronized{} 塊內(nèi)的代碼,這時候才會從 obj 房間出來并解開門上的鎖,喚醒 t2 線程并把鑰匙給他。t2 線程這時才可以進入 obj 房間,鎖住了門拿上鑰匙,執(zhí)行它的 count-- 代碼;
public class ExceptionDemo { private static int count = 1; // 創(chuàng)建鎖對象 private static Object object = new Object(); public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(()->{ synchronized (object){ System.out.println("線程t1正在執(zhí)行"); // 死循環(huán) while (count==1){ } } },"t1"); Thread t2 = new Thread(()->{ synchronized (object) { System.out.println("線程t2正在執(zhí)行"); count--; } },"t2"); t1.start(); t2.start(); t1.join(); t2.join(); } }
執(zhí)行結(jié)果:
可以看出線程t1執(zhí)行的是死循環(huán),所以每次線程上下文切換,線程t2都被阻塞了,拿不到鎖,從而無法執(zhí)行。
假如我們在線程執(zhí)行過程中制造一個異常:
public class ExceptionDemo { private static int count = 1; // 創(chuàng)建鎖對象 private static Object object = new Object(); public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(()->{ synchronized (object){ System.out.println("線程t1正在執(zhí)行"); while (count==1){ // 死循環(huán)中制造異常 Integer.parseInt("a"); } } },"t1"); Thread t2 = new Thread(()->{ synchronized (object) { System.out.println("線程t2正在執(zhí)行"); count--; } },"t2"); t1.start(); t2.start(); t1.join(); t2.join(); } }
執(zhí)行結(jié)果:
當(dāng)持有鎖對象的線程在執(zhí)行同步代碼快中的代碼時,如果出現(xiàn)異常,會釋放鎖,從而線程t2就可以拿到鎖對象去執(zhí)行自己同步代碼塊中的代碼了。
以上是“Java多線程之synchronized同步代碼塊的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學(xué)習(xí)更多知識,歡迎關(guān)注億速云行業(yè)資訊頻道!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。