1. 程式人生 > >Java併發程式設計系列之十:synchronized(1)

Java併發程式設計系列之十:synchronized(1)

在多執行緒併發訪問資源(這類資源稱為臨街資源)的時候,由於割裂來了原子操作,所以會導致資料不一致的情況。為了避免這種情況,需要使用同步機制,同步機制能夠保證多執行緒併發訪問資料的時候不會出現資料不一致的情況。

一種同步機制是使用synchronized關鍵字,這種機制也稱為互斥鎖機制,這就意味著同一時刻只能有一個執行緒能夠獲取到鎖,獲得的鎖也被稱為互斥鎖。其他需要獲取該互斥鎖的執行緒只能被阻塞,直到獲取到該鎖的執行緒釋放鎖。在Java中,每個類都有一個內建鎖,之所以如此,是因為Java併發專家認為這樣可以避免顯式建立鎖。

通過下面的程式碼可以直到synchronized到底是如何實現的:

package
com.rhwayfun.concurrency; /** * Created by rhwayfun on 16-4-3. */ public class Synchronied { public static void main(String[] args){ synchronized (Synchronied.class){ } a(); } public static synchronized void a() { } }

使用java -v Synchronized.class命令可以看到如下的結果:

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=3, args_size=1
         0: ldc           #2                  // class com/rhwayfun/concurrency/Synchronied
         2: dup
         3: astore_1
         4
: monitorenter 5: aload_1 6: monitorexit 7: goto 15 10: astore_2 11: aload_1 12: monitorexit 13: aload_2 14: athrow 15: invokestatic #3 // Method a:()V 18: return Exception table: from to target type 5 7 10 any 10 13 10 any LineNumberTable: line 8: 0 line 10: 5 line 11: 15 line 12: 18 LocalVariableTable: Start Length Slot Name Signature 0 19 0 args [Ljava/lang/String; public static synchronized void a(); descriptor: ()V flags: ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED Code: stack=0, locals=0, args_size=0 0: return LineNumberTable: line 15: 0 } SourceFile: "Synchronied.java"

通過編譯的class檔案可以看到synchronized程式碼塊使用了monitorenter和monitorexit兩個指令分別獲取鎖標記和釋放鎖標記,而synchronized方法使用了ACC_SYNCHRONIZED來完成鎖的獲取與釋放的。也就是鎖的獲取與釋放synchronized關鍵字自動幫我們完成了。

對於使用synchronized關鍵字實現的同步機制由如下幾點補充說明:

  1. 如果同一個方法有多個執行緒訪問,那麼每個執行緒都有自己的執行緒拷貝(拷貝儲存在工作記憶體中)
  2. 類的例項都有自己的物件鎖,如果一個執行緒成功獲取到該例項的物件鎖那麼當其他執行緒需要獲取該例項的物件鎖時,便需要阻塞等待,直到該例項的物件鎖被成功釋放。物件鎖可以作用在同步方法或者同步程式碼塊中
  3. 如果不同的執行緒訪問的是不同例項的物件鎖,那麼不會互相阻塞,因為不同例項的物件鎖是不同的
  4. 獲得例項的物件鎖的執行緒會讓其他想要獲取相同物件鎖的執行緒阻塞在synchronized程式碼外,比如由兩個synchronized方法a()、b(),那麼如果執行緒A成功進入了同步方法a(),那麼該執行緒便獲取了該例項(比如例項obj)的物件鎖,而如果其他執行緒想要執行另一個同步方法b(),就會阻塞在外面,因為a()和b()持有的都是物件obj的物件鎖
  5. 持有一個例項的物件鎖不會阻止該執行緒被置換出來,也不會阻塞其他執行緒執行非synchronized方法,因為非synchronized方法執行的時候不需要獲取例項的物件鎖
  6. 使用同步程式碼塊的時候,括號的物件可以為任意Object例項,當為this時,指的是當前物件的物件鎖
  7. 類鎖主要用於控制對static成員變數的併發訪問
  8. synchronized塊(可以是同步方法或者同步程式碼塊)是可重入的,每次重入會把鎖的計數器加1,每次退出將計數器減1,當計數器的值為0的時候,鎖便被釋放了
  9. Java SE 1.6 為了減少獲得鎖和釋放鎖的效能消耗引入了偏向鎖和輕量級鎖,所以使用synchronized也沒有那麼重量級了

相關推薦

Java併發程式設計系列synchronized1

在多執行緒併發訪問資源(這類資源稱為臨街資源)的時候,由於割裂來了原子操作,所以會導致資料不一致的情況。為了避免這種情況,需要使用同步機制,同步機制能夠保證多執行緒併發訪問資料的時候不會出現資料不一致的情況。 一種同步機制是使用synchronized關鍵字,

Java併發程式設計系列原子操作類

原子操作類簡介 當更新一個變數的時候,多出現資料爭用的時候可能出現所意想不到的情況。這時的一般策略是使用synchronized解決,因為synchronized能夠保證多個執行緒不會同時更新該變數。然而,從jdk 5之後,提供了粒度更細、量級更輕,並且在多核

Java併發程式設計系列阻塞佇列

阻塞佇列(BlockingQueue)是一個支援兩個附加操作的佇列。這兩個附加操作支援阻塞地插入和移除方法。支援阻塞插入的方法是指當佇列滿時會阻塞插入元素的執行緒,直到佇列不滿;支援阻塞移除的方法是指當佇列為空時獲取元素的執行緒無法繼續獲取元素直到佇列不空。

Java併發程式設計系列死鎖、飢餓和活鎖

死鎖發生在一個執行緒需要獲取多個資源的時候,這時由於兩個執行緒互相等待對方的資源而被阻塞,死鎖是最常見的活躍性問題。這裡先分析死鎖的情形: 假設當前情況是執行緒A已經獲取資源R1,執行緒B已經獲取資源R2,之後執行緒A嘗試獲取資源R2,這個時候因為資源R2已經

Java併發程式設計系列Lock鎖

Lock鎖簡介 Lock鎖機制是JDK 5之後新增的鎖機制,不同於內建鎖,Lock鎖必須顯式宣告,並在合適的位置釋放鎖。Lock是一個介面,其由三個具體的實現:ReentrantLock、ReetrantReadWriteLock.ReadLock 和 Ree

Java併發程式設計系列五 Executor框架

                     Java使用執行緒完成非同步任務是很普遍的事,而執行緒的建立與銷燬需要一定的開銷,如果每個任務都需要建立一個執行緒將會消耗大量的計算資源,JDK 5之後把工作單元和執行機制區分開了,工作單元包括Runnable和Callable,而執行機制則由Executor框架提供

Java併發程式設計系列七 Condition介面

                     通過前面的文章,我們知道任何一個Java物件,都擁有一組監視器方法,主要包括wait()、notify()、notifyAll()方法,這些方法與synchronized關鍵字配合使用可以實現等待/通知機制。而且前面我們已經使用這種方式實現了生產者-消費者模式。類似地

Java併發程式設計系列鎖與volatile的記憶體語義

前言 在前面的文章中已經提到過volatile關鍵字的底層實現原理:處理器的LOCK指令會使得其他處理器將快取重新整理到記憶體中(確切說是主存)以及會把其他處理器的快取設定為無效。這裡的記憶體語義則說的是在JMM中的實現,那麼為什麼要理解volatile和鎖在

Java併發程式設計系列二 死鎖 飢餓和活鎖

                        死鎖發生在一個執

Java併發程式設計系列happens-before原則

前言 happens-before是JMM的核心,之所以設計happens-before,主要出於以下兩個方面的因素考慮的:1)程式設計師的角度,JMM記憶體模型需要易於理解、易於程式設計;2)編譯器和處理器的角度,編譯器和處理器希望記憶體模型對其束縛越少越好

Java併發程式設計系列正確終止與恢復執行緒

前面提到了stop()、suspend()等方法在終止與恢復執行緒的弊端,那麼問題來了,應該如何正確終止與恢復執行緒呢?這裡可以使用兩種方法:interrupt()方法和使用boolean變數進行控制。 在使用interrupt方法之前,有必要介紹一下中斷以及

Java併發程式設計使用wait/notify/notifyAll實現執行緒間通訊的幾點重要說明

在Java中,可以通過配合呼叫Object物件的wait()方法和notify()方法或notifyAll()方法來實現執行緒間的通訊。線上程中呼叫wait()方法,將阻塞等待其他執行緒的通知(其

Java併發程式設計系列stop()、resume()和suspend()

這三個方法已經是jdk是過期的方法,為什麼仍然要單獨拿出來說呢?主要目的是理解jdk多執行緒API設計的初衷,理解並且更好使用執行緒API。那麼就來說說這三個方法吧:stop方法用於終止一個執行緒的執行,resume方法用於恢復執行緒的執行,suspend方法用

java設計模式系列設計模式概要1

而不是 行為型模式 一句話 創建 rom 多次 ati 代理模式 之間 一、什麽是設計模式   設計模式(Design pattern)是一套被反復使用、多數人知曉的、經過分類編目的、代碼設計經驗的總結。使用設計模式是為了可重用代碼、讓代碼更容易被他人理解、保證代碼可靠性。

Java併發程式設計系列ThreadLocal

ThreadLocal簡介 ThreadLocal翻譯過來就是執行緒本地變數,初學者可能以為ThreadLocal是指一個Thread,其實說白了,ThreadLocal就是一個成員變數,只不過這是一個特殊的變數——變數值總是與當前執行緒(呼叫Thread.c

Java併發程式設計系列CountdownLatch

CountDownLatch是JDK提供的併發工具包,理解並掌握這些工具包的使用有助於簡化特定場景下的程式設計。就CountDownLatch而言,允許一個或者多個執行緒等待其他執行緒完成操作。等待其他執行緒完成不是與Thread.join()方法類似嗎,因為T

Java併發程式設計系列丟失的訊號

這裡的丟失的訊號是指執行緒必須等待一個已經為真的條件,在開始等待之前沒有檢查等待條件。這種場景其實挺好理解,如果一邊燒水,一邊看電視,那麼在水燒開的時候,由於太投入而沒有注意到水被燒開。丟失的訊號指的就是這種情況。 建立兩個執行緒分別執行通知和等待方法,並且將

Java併發程式設計系列六 ConcurrentModificationException

                        在多執行緒程式的

Java併發程式設計系列 Fork/Join框架

                        Fork/Joi

Java併發程式設計深入Java記憶體模型——happen-before規則及其對DCL的分析含程式碼

happen—before規則介紹     Java語言中有一個“先行發生”(happen—before)的規則,它是Java記憶體模型中定義的兩項操作之間的偏序關係,如果操作A先行發生於操作B,其意思就是說,在發生操作B之前,操作A產生的影響都能被操作B觀察到,“影響