日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
Android進(jìn)階之徹底理解Synchronized關(guān)鍵字

[[417605]]

本文轉(zhuǎn)載自微信公眾號(hào)「Android開(kāi)發(fā)編程」,作者Android開(kāi)發(fā)編程。轉(zhuǎn)載本文請(qǐng)聯(lián)系A(chǔ)ndroid開(kāi)發(fā)編程公眾號(hào)。

為儀隴等地區(qū)用戶提供了全套網(wǎng)頁(yè)設(shè)計(jì)制作服務(wù),及儀隴網(wǎng)站建設(shè)行業(yè)解決方案。主營(yíng)業(yè)務(wù)為成都做網(wǎng)站、網(wǎng)站制作、儀隴網(wǎng)站設(shè)計(jì),以傳統(tǒng)方式定制建設(shè)網(wǎng)站,并提供域名空間備案等一條龍服務(wù),秉承以專業(yè)、用心的態(tài)度為用戶提供真誠(chéng)的服務(wù)。我們深信只要達(dá)到每一位用戶的要求,就會(huì)得到認(rèn)可,從而選擇與我們長(zhǎng)期合作。這樣,我們也可以走得更遠(yuǎn)!

一、Synchronized詳解

synchronized是Java中的一個(gè)關(guān)鍵字,在多線程共同操作共享資源的情況下,可以保證在同一時(shí)刻只有一個(gè)線程可以對(duì)共享資源進(jìn)行操作,從而實(shí)現(xiàn)共享資源的線程安全。

二、Synchronized的特性

  1. 原子性。synchronized可以確保多線程下對(duì)共享資源的互斥訪問(wèn),被synchronized作用的代碼可以實(shí)現(xiàn)原子性。
  2. 可見(jiàn)性。synchronized保證對(duì)共享資源的修改能夠及時(shí)被看見(jiàn)。在Java內(nèi)存模型中,對(duì)一個(gè)共享變量操作后進(jìn)行釋放鎖即進(jìn)行unlock操作前,必須將修改同步到主內(nèi)存中。如果對(duì)一個(gè)共享資源進(jìn)行加鎖即lock操作之前,必須將工作內(nèi)存中共享變量的值清空(因?yàn)槊恳粋€(gè)線程獲取的共享變量都是主存中共享變量的一個(gè)副本,如果不進(jìn)行清空,就會(huì)發(fā)生數(shù)據(jù)不一致,即當(dāng)前線程中的共享變量與主存中的共享變量不一致),在使用此共享變量時(shí),就需要從主存中重新加載此共享變量以獲得該共享變量最新的值。
  3. 有序性。synchronized可以有效解決重排序問(wèn)題,即一個(gè)unlock解鎖操作必定先行發(fā)生于后面線程對(duì)同一個(gè)鎖的lock操作,這樣就會(huì)保證主內(nèi)存值的共享變量永遠(yuǎn)是最新的。

三、Synchronized的使用

在應(yīng)用Sychronized關(guān)鍵字時(shí)需要把握如下注意點(diǎn):

一把鎖只能同時(shí)被一個(gè)線程獲取,沒(méi)有獲得鎖的線程只能等待;

每個(gè)實(shí)例都對(duì)應(yīng)有自己的一把鎖(this),不同實(shí)例之間互不影響;例外:鎖對(duì)象是*.class以及synchronized修飾的是static方法的時(shí)候,所有對(duì)象公用同一把鎖;

synchronized修飾的方法,無(wú)論方法正常執(zhí)行完畢還是拋出異常,都會(huì)釋放鎖。

對(duì)象鎖

包括方法鎖(默認(rèn)鎖對(duì)象為this,當(dāng)前實(shí)例對(duì)象)和同步代碼塊鎖(自己指定鎖對(duì)象鎖)

代碼塊形式:手動(dòng)指定鎖定對(duì)象,也可是是this,也可以是自定義的鎖

  
 
 
 
  1. public class SynchronizedObjectLock implements Runnable { static SynchronizedObjectLock instence = new SynchronizedObjectLock(); // 創(chuàng)建2把鎖 Object block1 = new Object(); Object block2 = new Object(); @Override public void run() { // 這個(gè)代碼塊使用的是第一把鎖,當(dāng)他釋放后,后面的代碼塊由于使用的是第二把鎖,因此可以馬上執(zhí)行 synchronized (block1) { System.out.println("block1鎖,我是線程" + Thread.currentThread().getName()); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("block1鎖,"+Thread.currentThread().getName() + "結(jié)束"); } synchronized (block2) { System.out.println("block2鎖,我是線程" + Thread.currentThread().getName()); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("block2鎖,"+Thread.currentThread().getName() + "結(jié)束"); } } public static void main(String[] args) { Thread t1 = new Thread(instence); Thread t2 = new Thread(instence); t1.start(); t2.start(); } } 復(fù)制代碼 

輸出結(jié)果:

  
 
 
 
  1. block1鎖,我是線程Thread-0 block1鎖,Thread-0結(jié)束 block2鎖,我是線程Thread-0  // 可以看到當(dāng)?shù)谝粋€(gè)線程在執(zhí)行完第一段同步代碼塊之后,第二個(gè)同步代碼塊可以馬上得到執(zhí)行,因?yàn)樗麄兪褂玫逆i不是同一把 block1鎖,我是線程Thread-1 block2鎖,Thread-0結(jié)束 block1鎖,Thread-1結(jié)束 block2鎖,我是線程Thread-1 block2鎖,Thread-1結(jié)束  

方法鎖形式:synchronized修飾普通方法,鎖對(duì)象默認(rèn)為this

  
 
 
 
  1. public class SynchronizedObjectLock implements Runnable { static SynchronizedObjectLock instence = new SynchronizedObjectLock(); @Override public void run() { method(); } public synchronized void method() { System.out.println("我是線程" + Thread.currentThread().getName()); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "結(jié)束"); } public static void main(String[] args) { Thread t1 = new Thread(instence); Thread t2 = new Thread(instence); t1.start(); t2.start(); } }  

類鎖

包括方法鎖(默認(rèn)鎖對(duì)象為this,當(dāng)前實(shí)例對(duì)象)和同步代碼塊鎖(自己指定鎖對(duì)象)

synchronize修飾靜態(tài)方法(類的class對(duì)象)

  
 
 
 
  1. public class SynchronizedObjectLock implements Runnable { static SynchronizedObjectLock instence1 = new SynchronizedObjectLock(); static SynchronizedObjectLock instence2 = new SynchronizedObjectLock(); @Override public void run() { method(); } // synchronized用在靜態(tài)方法上,默認(rèn)的鎖就是當(dāng)前所在的Class類,所以無(wú)論是哪個(gè)線程訪問(wèn)它,需要的鎖都只有一把 public static synchronized void method() { System.out.println("我是線程" + Thread.currentThread().getName()); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "結(jié)束"); } public static void main(String[] args) { Thread t1 = new Thread(instence1); Thread t2 = new Thread(instence2); t1.start(); t2.start(); } }  

輸出結(jié)果:

我是線程Thread-0 Thread-0結(jié)束 我是線程Thread-1 Thread-1結(jié)束 復(fù)制代碼

synchronized修改實(shí)例方法

  
 
 
 
  1. /** * synchronized修飾實(shí)例方法,當(dāng)線程拿到鎖,其他線程無(wú)法拿到該對(duì)象的鎖,那么其他線程就無(wú)法訪問(wèn)該對(duì)象的其他同步方法 * 但是可以訪問(wèn)該對(duì)象的其他非synchronized方法 * 鎖住的是類的實(shí)例對(duì)象 */ public class synchronizedDemo1 implements Runnable { //模擬一個(gè)共享數(shù)據(jù) private static int total=0; //同步方法,每個(gè)線程獲取到鎖之后,執(zhí)行5次累加操作 public synchronized void increase(){ for (int i = 1; i < 6; i++) { System.out.println(Thread.currentThread().getName()+"執(zhí)行累加操作..."+"第"+i+"次累加"); try { total=total+1; Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } } //實(shí)例對(duì)象的另一個(gè)同步方法 public synchronized void declare(){ System.out.println(Thread.currentThread().getName()+"執(zhí)行total-1"); total--; System.out.println(Thread.currentThread().getName()+"執(zhí)行total-1完成"); } //普通實(shí)例方法 public void simpleMethod(){ System.out.println(Thread.currentThread().getName()+ " ----實(shí)例對(duì)象的普通方法---"); } @Override public void run() { //線程執(zhí)行體 System.out.println(Thread.currentThread().getName()+"準(zhǔn)備執(zhí)行累加,還沒(méi)獲取到鎖"); //執(zhí)行普通方法 simpleMethod(); //調(diào)用同步方法執(zhí)行累加操作 increase(); //執(zhí)行完increase同步方法后,會(huì)釋放掉鎖,然后線程1和線程2會(huì)再一次進(jìn)行鎖的競(jìng)爭(zhēng),誰(shuí)先競(jìng)爭(zhēng)得到鎖,誰(shuí)就先執(zhí)行declare同步方法 System.out.println(Thread.currentThread().getName()+"完成累加操作"); //調(diào)用實(shí)例對(duì)象的另一個(gè)同步方法 System.out.println(Thread.currentThread().getName()+"準(zhǔn)備執(zhí)行total-1"); declare(); } public static void main(String[] args) throws InterruptedException { synchronizedDemo1 syn = new synchronizedDemo1(); Thread thread1 = new Thread(syn,"線程1"); Thread thread2 = new Thread(syn,"線程2"); thread1.start(); thread2.start(); } }  

輸出結(jié)果:

線程1準(zhǔn)備執(zhí)行累加,還沒(méi)獲取到鎖 線程2準(zhǔn)備執(zhí)行累加,還沒(méi)獲取到鎖 線程2 ----實(shí)例對(duì)象的普通方法--- 線程2執(zhí)行累加操作...第1次累加 //線程2通過(guò)與線程1的競(jìng)爭(zhēng)率先拿到了鎖,進(jìn)入increase同步方法 線程2執(zhí)行累加操作...第2次累加 線程1 ----實(shí)例對(duì)象的普通方法--- //從這里可看出,在線程2訪問(wèn)同步方法時(shí),線程1是可以訪問(wèn)非同步方法的,但是不可以訪問(wèn)另外一個(gè)同步方法 線程2執(zhí)行累加操作...第3次累加 線程2執(zhí)行累加操作...第4次累加 線程2執(zhí)行累加操作...第5次累加 線程2完成累加操作 //線程2執(zhí)行累加后會(huì)釋放掉鎖 線程2準(zhǔn)備執(zhí)行total-1 線程1執(zhí)行累加操作...第1次累加 //然后線程1拿到鎖后進(jìn)入increase同步方法執(zhí)行累加 線程1執(zhí)行累加操作...第2次累加 線程1執(zhí)行累加操作...第3次累加 線程1執(zhí)行累加操作...第4次累加 線程1執(zhí)行累加操作...第5次累加 線程1完成累加操作 //線程1完成累加操作也會(huì)釋放掉鎖,然后線程1和線程2會(huì)再進(jìn)行一次鎖競(jìng)爭(zhēng) 線程1準(zhǔn)備執(zhí)行total-1 線程2執(zhí)行total-1 //線程2通過(guò)競(jìng)爭(zhēng)率先拿到鎖進(jìn)入declear方法執(zhí)行total-1操作 線程2執(zhí)行total-1完成 線程1執(zhí)行total-1 線程1執(zhí)行total-1完成 復(fù)制代碼

四、Synchronized實(shí)現(xiàn)原理

加鎖和釋放鎖

synchronized同步是通過(guò)monitorenter和monitorexit等指令實(shí)現(xiàn)的,會(huì)讓對(duì)象在執(zhí)行,使其鎖計(jì)數(shù)器加1或者減1。

monitorenter指令:每一個(gè)對(duì)象在同一時(shí)間只與一個(gè)monitor(鎖)相關(guān)聯(lián),而一個(gè)monitor在同一時(shí)間只能被一個(gè)線程獲得,一個(gè)對(duì)象在嘗試獲得與這個(gè)對(duì)象相關(guān)聯(lián)的Monitor鎖的所有權(quán)的時(shí)候,會(huì)發(fā)生如下3種情況之一:

  • monitor計(jì)數(shù)器為0,意味著目前還沒(méi)有被獲得,那這個(gè)線程就會(huì)立刻獲得然后把鎖計(jì)數(shù)器+1,一旦+1,別的線程再想獲取,就需要等待
  • 如果這個(gè)monitor已經(jīng)拿到了這個(gè)鎖的所有權(quán),又重入了這把鎖,那鎖計(jì)數(shù)器就會(huì)累加,變成2,并且隨著重入的次數(shù),會(huì)一直累加
  • 若其他線程已經(jīng)持有了對(duì)象監(jiān)視器,則當(dāng)前線程進(jìn)入阻塞狀態(tài),直到對(duì)象監(jiān)視器的進(jìn)入數(shù)為0,重新嘗試獲取monitor的所有權(quán)。

monitorexit指令:釋放對(duì)于monitor的所有權(quán),釋放過(guò)程很簡(jiǎn)單,就是講monitor的計(jì)數(shù)器減1,如果減完以后,計(jì)數(shù)器不是0,則代表剛才是重入進(jìn)來(lái)的,當(dāng)前線程還繼續(xù)持有這把鎖的所有權(quán),如果計(jì)數(shù)器變成0,則代表當(dāng)前線程不再擁有該monitor的所有權(quán),即釋放鎖。

對(duì)象、對(duì)象監(jiān)視器、同步隊(duì)列以及執(zhí)行線程狀態(tài)之間的關(guān)系:

該圖可以看出,任意線程對(duì)Object的訪問(wèn),首先要獲得Object的監(jiān)視器,如果獲取失敗,該線程就進(jìn)入同步狀態(tài),線程狀態(tài)變?yōu)锽LOCKED,當(dāng)Object的監(jiān)視器占有者釋放后,在同步隊(duì)列中得線程就會(huì)有機(jī)會(huì)重新獲取該監(jiān)視器。

可重入原理:加鎖次數(shù)計(jì)數(shù)器

從上圖中就可以看出來(lái),執(zhí)行靜態(tài)同步方法的時(shí)候就只有一條monitorexit指令,并沒(méi)有monitorenter獲取鎖的指令。這就是鎖的重入性,即在同一鎖程中,線程不需要再次獲取同一把鎖。

Synchronized先天具有重入性。每個(gè)對(duì)象擁有一個(gè)計(jì)數(shù)器,當(dāng)線程獲取該對(duì)象鎖后,計(jì)數(shù)器就會(huì)加一,釋放鎖后就會(huì)將計(jì)數(shù)器減一。

保證可見(jiàn)性的原理:內(nèi)存模型和happens-before規(guī)則

Synchronized的happens-before規(guī)則,即監(jiān)視器鎖規(guī)則:對(duì)同一個(gè)監(jiān)視器的解鎖,happens-before于對(duì)該監(jiān)視器的加鎖。

public class MonitorDemo { private int a = 0; public synchronized void writer() { // 1 a++; // 2 } // 3 public synchronized void reader() { // 4 int i = a; // 5 } // 6 } 復(fù)制代碼

happens-before關(guān)系如圖所示:

在圖中每一個(gè)箭頭連接的兩個(gè)節(jié)點(diǎn)就代表之間的happens-before關(guān)系,黑色的是通過(guò)程序順序規(guī)則推導(dǎo)出來(lái),紅色的為監(jiān)視器鎖規(guī)則推導(dǎo)而出:線程A釋放鎖happens-before線程B加鎖,藍(lán)色的則是通過(guò)程序順序規(guī)則和監(jiān)視器鎖規(guī)則推測(cè)出來(lái)happens-befor關(guān)系,通過(guò)傳遞性規(guī)則進(jìn)一步推導(dǎo)的happens-before關(guān)系。

總結(jié)

  • synchronized同步語(yǔ)句塊的實(shí)現(xiàn)使?的是monitorenter和monitorexit指令,其中monitorenter指令指向同步代碼塊的開(kāi)始位置, monitorexit指令則指明同步代碼塊的結(jié)束位置。
  • synchronized修飾的?法并沒(méi)有 monitorenter 指令和 monitorexit 指令,取得代之的確實(shí)是ACC_SYNCHRONIZED 標(biāo)識(shí),該標(biāo)識(shí)指明了該?法是?個(gè)同步?法。

不過(guò)兩者的本質(zhì)都是對(duì)對(duì)象監(jiān)視器 monitor 的獲取。

使用Synchronized有哪些要注意的?

  • 鎖對(duì)象不能為空,因?yàn)殒i的信息都保存在對(duì)象頭里;
  • 作用域不宜過(guò)大,影響程序執(zhí)行的速度,控制范圍過(guò)大,編寫(xiě)代碼也容易出錯(cuò);
  • 避免死鎖;
  • 在能選擇的情況下,既不要用Lock也不要用synchronized關(guān)鍵字,用java.util.concurrent包中的各種各樣的類,如果不用該包下的類,在滿足業(yè)務(wù)的情況下,可以使用synchronized關(guān)鍵字,因?yàn)榇a量少,避免出錯(cuò)。

本文題目:Android進(jìn)階之徹底理解Synchronized關(guān)鍵字
本文網(wǎng)址:http://m.5511xx.com/article/cdssodp.html