成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Android進階之徹底理解Synchronized關鍵字

移動開發 Android
synchronized是Java中的一個關鍵字,在多線程共同操作共享資源的情況下,可以保證在同一時刻只有一個線程可以對共享資源進行操作,從而實現共享資源的線程安全。

[[417605]]

本文轉載自微信公眾號「Android開發編程」,作者Android開發編程。轉載本文請聯系Android開發編程公眾號。

一、Synchronized詳解

synchronized是Java中的一個關鍵字,在多線程共同操作共享資源的情況下,可以保證在同一時刻只有一個線程可以對共享資源進行操作,從而實現共享資源的線程安全。

二、Synchronized的特性

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

三、Synchronized的使用

在應用Sychronized關鍵字時需要把握如下注意點:

一把鎖只能同時被一個線程獲取,沒有獲得鎖的線程只能等待;

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

synchronized修飾的方法,無論方法正常執行完畢還是拋出異常,都會釋放鎖。

對象鎖

包括方法鎖(默認鎖對象為this,當前實例對象)和同步代碼塊鎖(自己指定鎖對象鎖)

代碼塊形式:手動指定鎖定對象,也可是是this,也可以是自定義的鎖

  1. public class SynchronizedObjectLock implements Runnable { static SynchronizedObjectLock instence = new SynchronizedObjectLock(); // 創建2把鎖 Object block1 = new Object(); Object block2 = new Object(); @Override public void run() { // 這個代碼塊使用的是第一把鎖,當他釋放后,后面的代碼塊由于使用的是第二把鎖,因此可以馬上執行 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() + "結束"); } 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() + "結束"); } } public static void main(String[] args) { Thread t1 = new Thread(instence); Thread t2 = new Thread(instence); t1.start(); t2.start(); } } 復制代碼 

輸出結果:

  1. block1鎖,我是線程Thread-0 block1鎖,Thread-0結束 block2鎖,我是線程Thread-0  // 可以看到當第一個線程在執行完第一段同步代碼塊之后,第二個同步代碼塊可以馬上得到執行,因為他們使用的鎖不是同一把 block1鎖,我是線程Thread-1 block2鎖,Thread-0結束 block1鎖,Thread-1結束 block2鎖,我是線程Thread-1 block2鎖,Thread-1結束  

方法鎖形式:synchronized修飾普通方法,鎖對象默認為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() + "結束"); } public static void main(String[] args) { Thread t1 = new Thread(instence); Thread t2 = new Thread(instence); t1.start(); t2.start(); } }  

類鎖

包括方法鎖(默認鎖對象為this,當前實例對象)和同步代碼塊鎖(自己指定鎖對象)

synchronize修飾靜態方法(類的class對象)

  1. public class SynchronizedObjectLock implements Runnable { static SynchronizedObjectLock instence1 = new SynchronizedObjectLock(); static SynchronizedObjectLock instence2 = new SynchronizedObjectLock(); @Override public void run() { method(); } // synchronized用在靜態方法上,默認的鎖就是當前所在的Class類,所以無論是哪個線程訪問它,需要的鎖都只有一把 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() + "結束"); } public static void main(String[] args) { Thread t1 = new Thread(instence1); Thread t2 = new Thread(instence2); t1.start(); t2.start(); } }  

輸出結果:

我是線程Thread-0 Thread-0結束 我是線程Thread-1 Thread-1結束 復制代碼

synchronized修改實例方法

  1. /** * synchronized修飾實例方法,當線程拿到鎖,其他線程無法拿到該對象的鎖,那么其他線程就無法訪問該對象的其他同步方法 * 但是可以訪問該對象的其他非synchronized方法 * 鎖住的是類的實例對象 */ public class synchronizedDemo1 implements Runnable { //模擬一個共享數據 private static int total=0; //同步方法,每個線程獲取到鎖之后,執行5次累加操作 public synchronized void increase(){ for (int i = 1; i < 6; i++) { System.out.println(Thread.currentThread().getName()+"執行累加操作..."+"第"+i+"次累加"); try { total=total+1; Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } } //實例對象的另一個同步方法 public synchronized void declare(){ System.out.println(Thread.currentThread().getName()+"執行total-1"); total--; System.out.println(Thread.currentThread().getName()+"執行total-1完成"); } //普通實例方法 public void simpleMethod(){ System.out.println(Thread.currentThread().getName()+ " ----實例對象的普通方法---"); } @Override public void run() { //線程執行體 System.out.println(Thread.currentThread().getName()+"準備執行累加,還沒獲取到鎖"); //執行普通方法 simpleMethod(); //調用同步方法執行累加操作 increase(); //執行完increase同步方法后,會釋放掉鎖,然后線程1和線程2會再一次進行鎖的競爭,誰先競爭得到鎖,誰就先執行declare同步方法 System.out.println(Thread.currentThread().getName()+"完成累加操作"); //調用實例對象的另一個同步方法 System.out.println(Thread.currentThread().getName()+"準備執行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(); } }  

輸出結果:

線程1準備執行累加,還沒獲取到鎖 線程2準備執行累加,還沒獲取到鎖 線程2 ----實例對象的普通方法--- 線程2執行累加操作...第1次累加 //線程2通過與線程1的競爭率先拿到了鎖,進入increase同步方法 線程2執行累加操作...第2次累加 線程1 ----實例對象的普通方法--- //從這里可看出,在線程2訪問同步方法時,線程1是可以訪問非同步方法的,但是不可以訪問另外一個同步方法 線程2執行累加操作...第3次累加 線程2執行累加操作...第4次累加 線程2執行累加操作...第5次累加 線程2完成累加操作 //線程2執行累加后會釋放掉鎖 線程2準備執行total-1 線程1執行累加操作...第1次累加 //然后線程1拿到鎖后進入increase同步方法執行累加 線程1執行累加操作...第2次累加 線程1執行累加操作...第3次累加 線程1執行累加操作...第4次累加 線程1執行累加操作...第5次累加 線程1完成累加操作 //線程1完成累加操作也會釋放掉鎖,然后線程1和線程2會再進行一次鎖競爭 線程1準備執行total-1 線程2執行total-1 //線程2通過競爭率先拿到鎖進入declear方法執行total-1操作 線程2執行total-1完成 線程1執行total-1 線程1執行total-1完成 復制代碼

四、Synchronized實現原理

加鎖和釋放鎖

synchronized同步是通過monitorenter和monitorexit等指令實現的,會讓對象在執行,使其鎖計數器加1或者減1。

monitorenter指令:每一個對象在同一時間只與一個monitor(鎖)相關聯,而一個monitor在同一時間只能被一個線程獲得,一個對象在嘗試獲得與這個對象相關聯的Monitor鎖的所有權的時候,會發生如下3種情況之一:

  • monitor計數器為0,意味著目前還沒有被獲得,那這個線程就會立刻獲得然后把鎖計數器+1,一旦+1,別的線程再想獲取,就需要等待
  • 如果這個monitor已經拿到了這個鎖的所有權,又重入了這把鎖,那鎖計數器就會累加,變成2,并且隨著重入的次數,會一直累加
  • 若其他線程已經持有了對象監視器,則當前線程進入阻塞狀態,直到對象監視器的進入數為0,重新嘗試獲取monitor的所有權。

monitorexit指令:釋放對于monitor的所有權,釋放過程很簡單,就是講monitor的計數器減1,如果減完以后,計數器不是0,則代表剛才是重入進來的,當前線程還繼續持有這把鎖的所有權,如果計數器變成0,則代表當前線程不再擁有該monitor的所有權,即釋放鎖。

對象、對象監視器、同步隊列以及執行線程狀態之間的關系:

該圖可以看出,任意線程對Object的訪問,首先要獲得Object的監視器,如果獲取失敗,該線程就進入同步狀態,線程狀態變為BLOCKED,當Object的監視器占有者釋放后,在同步隊列中得線程就會有機會重新獲取該監視器。

可重入原理:加鎖次數計數器

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

Synchronized先天具有重入性。每個對象擁有一個計數器,當線程獲取該對象鎖后,計數器就會加一,釋放鎖后就會將計數器減一。

保證可見性的原理:內存模型和happens-before規則

Synchronized的happens-before規則,即監視器鎖規則:對同一個監視器的解鎖,happens-before于對該監視器的加鎖。

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 } 復制代碼

happens-before關系如圖所示:

在圖中每一個箭頭連接的兩個節點就代表之間的happens-before關系,黑色的是通過程序順序規則推導出來,紅色的為監視器鎖規則推導而出:線程A釋放鎖happens-before線程B加鎖,藍色的則是通過程序順序規則和監視器鎖規則推測出來happens-befor關系,通過傳遞性規則進一步推導的happens-before關系。

總結

  • synchronized同步語句塊的實現使?的是monitorenter和monitorexit指令,其中monitorenter指令指向同步代碼塊的開始位置, monitorexit指令則指明同步代碼塊的結束位置。
  • synchronized修飾的?法并沒有 monitorenter 指令和 monitorexit 指令,取得代之的確實是ACC_SYNCHRONIZED 標識,該標識指明了該?法是?個同步?法。

不過兩者的本質都是對對象監視器 monitor 的獲取。

使用Synchronized有哪些要注意的?

  • 鎖對象不能為空,因為鎖的信息都保存在對象頭里;
  • 作用域不宜過大,影響程序執行的速度,控制范圍過大,編寫代碼也容易出錯;
  • 避免死鎖;
  • 在能選擇的情況下,既不要用Lock也不要用synchronized關鍵字,用java.util.concurrent包中的各種各樣的類,如果不用該包下的類,在滿足業務的情況下,可以使用synchronized關鍵字,因為代碼量少,避免出錯。

 

責任編輯:武曉燕 來源: Android開發編程
相關推薦

2019-12-20 15:19:41

Synchroinze線程安全

2024-03-15 15:12:27

關鍵字底層代碼

2021-03-10 15:59:39

JavaSynchronize并發編程

2022-01-26 00:03:00

關鍵字線程JVM

2022-06-29 08:05:25

Volatile關鍵字類型

2024-11-20 15:55:57

線程Java開發

2021-09-04 07:29:57

Android

2017-05-27 20:59:30

Java多線程synchronize

2021-01-12 09:22:18

Synchronize線程開發技術

2011-07-14 23:14:42

C++static

2009-08-12 13:37:01

Java synchr

2019-09-04 14:14:52

Java編程數據

2009-12-18 11:37:54

Ruby關鍵字yiel

2009-06-29 18:26:11

Java多線程Synchronize同步類

2024-03-15 11:52:03

C++關鍵字編程

2023-11-10 09:29:30

MySQLExplain

2024-12-26 00:28:59

C#base?關鍵字

2023-10-04 00:04:00

C++extern

2019-12-10 13:55:10

Go指針存儲

2011-03-09 14:36:44

synchronizevolatile
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 欧美精品成人一区二区三区四区 | 国产高清免费视频 | 视频1区2区 | 久久久精品网站 | 91精品国产91 | 日韩欧美一二三区 | 免费天天干 | 久久伊 | 亚洲精品一 | 精品国产乱码久久久久久蜜柚 | 亚洲一区二区三区免费 | 在线免费看黄 | av日韩在线播放 | 午夜精品久久久久久久久久久久久 | 日韩一区二区在线视频 | 亚洲精品久久久久久国产精华液 | 黄色av网站在线观看 | 精品国产精品国产偷麻豆 | 成人精品在线视频 | 亚洲成人在线免费 | 日韩国产精品一区二区三区 | 日本在线免费视频 | 91av视频在线播放 | 日韩精品视频一区二区三区 | 欧美一区二区三区大片 | 日本免费视频 | 国产综合精品一区二区三区 | 在线午夜电影 | 91秦先生艺校小琴 | 国产精品亚洲精品 | 久久人人爽人人爽人人片av免费 | 国产伦精品一区二区三区四区视频 | 91精品久久久 | 亚洲成人在线视频播放 | 91麻豆精品国产91久久久更新资源速度超快 | a级毛片国产 | 粉嫩国产精品一区二区在线观看 | 欧美黄色一区 | 五月婷婷丁香 | 免费一级片 | 无毛av|