1. 程式人生 > >Java-併發-鎖-synchronized

Java-併發-鎖-synchronized

Java-併發-鎖-synchronized

摘要

本文會詳細說下synchronized的底層實現原理。

0x01 基本概念

  • 每次只能有一個執行緒進入臨界區
  • 保證臨界區內共享變數的可見性和有序性
  • 成功進入synchronized區域的執行緒可以拿到物件的Object-Monitor。具體有3種用法,作用域不同,在後面例子中介紹。
  • 對於拿到鎖的執行緒來說,同一個物件的synchronized具有可重入性
  • 不要將可變物件作為synchronized
  • 如果相互等待對方的synchronized 物件,可能出現死鎖
  • synchronized鎖是非公平的

注意:最近發現本文所講偏向鎖和輕量級鎖的程式碼分析章節有誤,請大家移駕參閱死磕Synchronized底層實現–概論系列文章,檢視原始碼分析。待後續有時間我會改正本文內容。

前置知識,是鎖優化升級:

  • Java中4種synchronized鎖狀態,隨著鎖競爭開始,這幾種鎖之間有鎖升級的關係:
    無鎖 -> 偏向鎖 -> 輕量級鎖 -> 重量級鎖
  • 鎖只能升級不能降級。這麼做的原因是縮短鎖的獲取釋放週期,提升效率。

關於鎖優化和鎖升級知識的更多詳細內容請點選此文:Java-併發-關於鎖的一切

0x02 實現原理

2.1 引子-synchronized用於物件同步塊

可以用一個簡單程式碼試試:

public class SimpleTest2
{

    public static void main(String[] args)
    {
        Object chengc = new Object();
        synchronized (chengc){
            int i = 1;
            System.out.println("");
        }
    }
}

然後就是用命令編譯:

$ javac SimpleTest2.java

然後進行反彙編:

$ javap -c SimpleTest2.class

結果如下:

11: monitorenter
12: iconst_1
13: istore_3
14: aload_2
15: monitorexit

synchronized程式碼塊通過javap生成的位元組碼中包含 monitorentermonitorexit指令。也就是說,synchronized指令可以嘗試獲取物件鎖(object-monitor):

  • monitor
    每個物件都關聯著一個monitor,只能被唯一獲取monitor許可權的執行緒鎖定。鎖定後,其他執行緒請求會失敗,進入等待集合,執行緒隨之被阻塞。
  • monitorenter
    這個命令就是用來獲取監視器的許可權,每進入1次就記錄次數加1,也就是同一執行緒說可重入。而其他未獲取到鎖的只能等待。
  • monitorexit
    擁有該監視器的執行緒才能使用該指令,且每次執行都會將累計的計數減1,直到變為0就釋放了所有權。在此之後其他等待的執行緒就開始競爭該監視器的許可權

jdk8/hotspot/src/share/vm/interpreter/interpreterRuntime.hpp中可以找到如下方法:

static void    monitorenter(JavaThread* thread, BasicObjectLock* elem);
static void    monitorexit (JavaThread* thread, BasicObjectLock* elem);

因為前置知識還不夠,我們程式碼分析先說到這裡。請看完第四章後,在這裡繼續看monitorenter底層實現。

2.2 synchronized用於方法

先說結論,將關鍵字修飾方法,那就會使得該方法的flags中多出一個ACC_SYNCHRONIZED。當執行緒執行時就需要去獲取物件監視器,拿到的再開始執行方法。

例項如下:

public class SynchronizedMethodTest
{
    public synchronized void synMethod(){
        int j = 11;
    }

    public static synchronized void method() {
        System.out.println("Hello World!");
    }
}

編譯後再使用javap -verbose SynchronizedMethodTest.class,部分結果如下:

public synchronized void synMethod();
  descriptor: ()V
  flags: ACC_PUBLIC, ACC_SYNCHRONIZED
  Code:
    stack=1, locals=2, args_size=1
       0: bipush        11
       2: istore_1
       3: return
    LineNumberTable:
      line 9: 0
      line 10: 3

public static synchronized void method();
  descriptor: ()V
  flags: ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED
  Code:
    stack=2, locals=0, args_size=0
       0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #3                  // String Hello World!
       5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: return
    LineNumberTable:
      line 13: 0
      line 14: 8

可以看到兩個synchronized方法的flags區域都多了ACC_SYNCHRONIZED。有這種標識的,會被JVM要求執行方法之前先獲取到鎖,否則等待阻塞。

2.3 synchronized用於靜態方法或類物件

這種情況的ObjectMonitor就是該Class物件。

2.4 ObjectMonitor

每個物件都有一個物件監視器。

Object-Monitor在HotSpot中實現主要在

  • /Users/chengc/cc/work/projects/jdk8/hotspot/src/share/vm/runtime/objectMonitor.hpp
  • /Users/chengc/cc/work/projects/jdk8/hotspot/src/share/vm/runtime/objectMonitor.cpp
    這裡摘錄一些重要的部分:
ObjectMonitor() {
    _header       = NULL;
    _count        = 0;
    _waiters      = 0,
    _recursions   = 0;
    _object       = NULL;
    _owner        = NULL;
    _WaitSet      = NULL;
    _WaitSetLock  = 0 ;
    _Responsible  = NULL ;
    _succ         = NULL ;
    _cxq          = NULL ;
    FreeNext      = NULL ;
    _EntryList    = NULL ;
    _SpinFreq     = 0 ;
    _SpinClock    = 0 ;
    OwnerIsThread = 0 ;
    _previous_owner_tid = 0;
  }

注意_WaitSett和_EntryList,他們被用來儲存該Object的等待物件的集合,具體來說:

  • _count記錄已獲取鎖的次數
  • _WaitSet存放處於wait狀態的執行緒
  • _EntryList存放那些等待synchronized同步鎖的執行緒
  • _owner是指向獲得了該物件的Monitor許可權的執行緒的指標。
  • _WaitSetLock用來對WaitSet進行同步保護

他們的關係示意圖如下:
ObjectMonitor

  1. 執行synchronized,進入EntrySet,執行緒處於等待狀態
  2. 每次都只能有一個執行緒獲取到ObjectMonitor_owner指向該執行緒,處於Active狀態。同時,將monitor計數器加1(退出一個同步塊時,monitor計數器減1)
  3. 執行wait方法,會釋放ObjectMonitor,進入WaitSet,執行緒處於等待狀態。同時,_owner置為空,monitor計數器歸0。
  4. 執行當擁有ObjectMonitor許可權的執行緒釋放後會,如果呼叫了notify或notifyAll方法,會喚醒WaitSet中的執行緒。被喚醒的執行緒重新競爭同步鎖。
  5. 當退出synchronized塊後,完全釋放ObjectMonitor

2.5 ObjectWaiter

而EntryList和WaitSet中的等待物件ObjectWaiter實現如下:

class ObjectWaiter : public StackObj {
 public:
  enum TStates { TS_UNDEF, TS_READY, TS_RUN, TS_WAIT, TS_ENTER, TS_CXQ } ;
  enum Sorted  { PREPEND, APPEND, SORTED } ;
  ObjectWaiter * volatile _next;
  ObjectWaiter * volatile _prev;
  Thread*       _thread;
  jlong         _notifier_tid;
  ParkEvent *   _event;
  volatile int  _notified ;
  volatile TStates TState ;
  Sorted        _Sorted ;           // List placement disposition
  bool          _active ;           // Contention monitoring is enabled
 public:
  ObjectWaiter(Thread* thread);

  void wait_reenter_begin(ObjectMonitor *mon);
  void wait_reenter_end(ObjectMonitor *mon);
};
  • TStates
    存放當前執行緒狀態
  • _next和_prev
    可以看到該物件有兩個分別指向前一個元素和後一個元素的指標,也就是說是一個雙向連結串列。
  • _thread
    指向的執行緒

2.6 monitorenter原始碼分析

2.6.1 位元組碼直譯器與模板直譯器

HotSpot程式碼中有兩個直譯器來解析jvm指令。前者是用C++實現每條JVM指令,但執行較慢。程式碼在jdk8/hotspot/src/share/vm/interpreter/bytecodeInterpreter.cpp;後者(可參考這篇文章:JVM之模板直譯器)對每個指令都寫了一段彙編,啟動時指令與彙編繫結,效率很高。程式碼在jdk8/hotspot/src/cpu/x86/vm/templateTable_x86_64.cpp

但在HotSpot中只用了模板直譯器,所以我們直接看templateTable_x86_64.cpp,我們要看的monitorentermonitorexit方法就在這裡定義。

2.6.2 TemplateTable::monitorenter

2.6.1 InterpreterRuntime::monitorenter

對應到jdk8/hotspot/src/share/vm/interpreter/interpreterRuntime.cpp繼續看看monitorenter方法實現,這裡摘錄部分對我們分析有意義的程式碼如下:

IRT_ENTRY_NO_ASYNC(void, InterpreterRuntime::monitorenter(JavaThread* thread, BasicObjectLock* elem))
// 這個h_obj封裝了該執行緒和擁有鎖的物件
Handle h_obj(thread, elem->obj());
if (UseBiasedLocking) {
  // 當開啟了偏向鎖模式時,就優先使用快速進入模式避免不必要的鎖膨脹
  ObjectSynchronizer::fast_enter(h_obj, elem->lock(), true, CHECK);
} else {
  ObjectSynchronizer::slow_enter(h_obj, elem->lock(), CHECK);
}

2.6.2 BasicObjectLock

上面的monitorenter方法有兩個引數:

  • thread:
    JavaThread* thread:指向我們當前的執行緒
  • BasicObjectLock* elem:
    找到BasicObjectLock位於jdk8/hotspot/src/share/vm/runtime/basicLock.hpp
// 一個BasicObjectLock將一個特定的Java物件與一個BasicLock相關聯,
// 它目前被嵌入到直譯器框架中。
class BasicObjectLock VALUE_OBJ_CLASS_SPEC {
  friend class VMStructs;
 private:
  // 鎖物件,必須雙字(一般一個字是4位元組)對齊
  BasicLock _lock;
  // 持有該鎖的物件                                    
  oop       _obj;                                     
  • 繼續看BasicLock,摘錄部分程式碼如下:
class BasicLock VALUE_OBJ_CLASS_SPEC {
  friend class VMStructs;
 private:
  // 該私有變數_displaced_header就是用來描述物件頭資訊的
  volatile markOop _displaced_header;
  public:
   markOop      displaced_header() const               { return _displaced_header; }
   // 儲存物件頭的方法
   void         set_displaced_header(markOop header)   { _displaced_header = header; }
}

2.6.3 鎖升級

注意前面我們提到的monitorenter方法中核心程式碼:

if (UseBiasedLocking) {
  // 當開啟了偏向鎖模式時,就優先使用快速進入模式避免不必要的鎖膨脹
  ObjectSynchronizer::fast_enter(h_obj, elem->lock(), true, CHECK);
} else {
  ObjectSynchronizer::slow_enter(h_obj, elem->lock(), CHECK);
}

也就是說,從這裡開始,我們的synchronized就要從偏向鎖開啟鎖升級之旅(前提是開啟了偏向鎖設定,否則從輕量級鎖開始)。

2.7 偏向鎖

2.7.1 偏向鎖的獲取

2.7.1.1 fast_enter
// 快速獲取/釋放monitor所有權
// 該方法在競爭場景下十分敏感
void ObjectSynchronizer::fast_enter(Handle obj, BasicLock* lock, bool attempt_rebias, TRAPS) {
 // 判斷是否使用偏向鎖
 if (UseBiasedLocking) {
    if (!SafepointSynchronize::is_at_safepoint()) {
    // 不在安全點,呼叫revoke_and_rebias方法獲取偏向鎖
      BiasedLocking::Condition cond = BiasedLocking::revoke_and_rebias(obj, attempt_rebias, THREAD);
      if (cond == BiasedLocking::BIAS_REVOKED_AND_REBIASED) {
        return;
      }
    } else {
    // 處於安全點,呼叫revoke_at_safepoint釋放偏向鎖
      assert(!attempt_rebias, "can not rebias toward VM thread");
      BiasedLocking::revoke_at_safepoint(obj);
    }
    assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now");
 }
 // 除非不在安全點,獲取了偏向鎖。其他情況都會走到這一步,採用slow_enter輕量級鎖
 slow_enter (obj, lock, THREAD) ;
}
2.7.1.2 biasedLocking.revoke_and_rebias

revoke_and_rebias方法程式碼位於jdk8/hotspot/src/share/vm/runtime/biasedLocking.cpp,主要做的就是獲取偏向鎖。確實因為能力有限看不懂了 - - 。這裡轉載佔小狼大神JVM原始碼分析之synchronized實現一文對該過程的描述,略有改動:

  1. 通過markOop mark = obj->mark()獲取物件的物件頭的Mark Word
  2. 判斷mark是否為可偏向狀態,即mark的偏向鎖標誌位為 1且鎖標誌位為 01;
  3. 判斷markJavaThread指標的狀態:
    • 如果為空,則進入步驟(4);
    • 如果指向當前執行緒,則執行同步程式碼塊;
    • 如果指向其它執行緒,進入步驟(5);
  4. 通過CAS原子指令設定mark中的JavaThread為當前執行緒ID。如果執行CAS成功,則執行同步程式碼塊,否則進入步驟(5);
  5. 如果執行CAS失敗,表示當前存在多個執行緒競爭鎖。當達到全域性安全點(safepoint),獲得偏向鎖的執行緒被掛起,撤銷偏向鎖,並升級為輕量級鎖。升級完成後被阻塞在安全點的執行緒繼續執行同步程式碼塊。

2.7.2 偏向鎖的釋放

前面提到過,當已經位於安全點(is_at_safepoint),才可以釋放偏向鎖。

2.7.2.1 BiasedLocking::revoke_at_safepoint
void BiasedLocking::revoke_at_safepoint(Handle h_obj) {
  //又一次斷言,只允許在安全點呼叫該方法
  assert(SafepointSynchronize::is_at_safepoint(), "must only be called while at safepoint");
  // 封裝了執行緒和鎖物件
  oop obj = h_obj();
  // 這一步作用是更新釋放偏向鎖的計數
  HeuristicsResult heuristics = update_heuristics(obj, false);
  if (heuristics == HR_SINGLE_REVOKE) {
  // 單撤銷模式
    revoke_bias(obj, false, false, NULL);
  } else if ((heuristics == HR_BULK_REBIAS) ||
             (heuristics == HR_BULK_REVOKE)) {
    // 批量撤銷模式(因為heuristics == HR_BULK_REBIAS為false)
    bulk_revoke_or_rebias_at_safepoint(obj, (heuristics == HR_BULK_REBIAS), false, NULL);
  }
  // 遍歷執行緒列表,清除所有monitor鎖快取資訊
  clean_up_cached_monitor_info();
}
2.7.2.2 BiasedLocking::Condition revoke_bias

核心程式碼如下:

// 引數1為鎖物件和執行緒組合
// 引數2為是否允許偏向
// 引數3為是否批量模式
// 引數4申請執行緒
static BiasedLocking::Condition revoke_bias(oop obj, bool allow_rebias, bool is_bulk, JavaThread* requesting_thread) {
  markOop mark = obj->mark();
  if (!mark->has_bias_pattern()) {
    // 非偏向鎖模式直接返回無偏向
    return BiasedLocking::NOT_BIASED;
  }
  uint age = mark->age();
  // 該值代表MarkWord最後3bit101
  markOop   biased_prototype = markOopDesc::biased_locking_prototype()->set_age(age);
  // 該值代表MarkWord最後3bit001
  markOop unbiased_prototype = markOopDesc::prototype()->set_age(age);
  // 擁有偏向鎖的執行緒
  JavaThread* biased_thread = mark->biased_locker();
  if (biased_thread == NULL) {
  // 此時偏向鎖沒有指向任何執行緒,可能是hashcode誤差等原因
    if (!allow_rebias) {
    // 不允許偏向,就設MarkWord為非偏向模式(無鎖,倒數第三bit設為0,最後2bit 01)
      obj->set_mark(unbiased_prototype);
    }
    // 返回已撤銷偏向鎖
    return BiasedLocking::BIAS_REVOKED;
  }
  
  bool thread_is_alive = false;
  if (requesting_thread == biased_thread) {
  // 看當前申請偏向鎖執行緒是否就是擁有偏向鎖的執行緒
    thread_is_alive = true;
  } else {
  // 單撤銷偏向鎖的時候,requesting_thread為null
  // 就遍歷所有執行緒
    for (JavaThread* cur_thread = Threads::first(); cur_thread != NULL; cur_thread = cur_thread->next()) {
      if (cur_thread == biased_thread) {
      // 找到了擁有偏向鎖的執行緒,跳出迴圈
        thread_is_alive = true;
        break;
      }
    }
  }
  
  if (!thread_is_alive) {
  // 擁有偏向鎖的執行緒已經掛了
    if (allow_rebias) {
    // 允許申請,就直接設為匿名可偏向模式,即MarkWord中執行緒id設為null,最後2bit 01
      obj->set_mark(biased_prototype);
    } else {
    // 否則設為非偏向模式(無鎖,倒數第三bit設為0,最後2bit 01)
      obj->set_mark(unbiased_prototype);
    }
    // 返回偏向鎖已經撤銷
    return BiasedLocking::BIAS_REVOKED;
  }
  // 來到這裡,說明擁有偏向鎖的執行緒還活著
  // 後面一系列程式碼做的事
  // 先檢查該執行緒是否擁有monitor,如果是就將所需的`displaced headers`寫入執行緒的棧中
  // 否則恢復鎖物件的頭資訊為
  if (allow_rebias) {
      // 設定為匿名偏向狀態
      obj->set_mark(biased_prototype);
    } else {
      // 設為無鎖
      obj->set_mark(unbiased_prototype);
  }
  // 最後返回鎖已經撤銷
  return BiasedLocking::BIAS_REVOKED;
}

2.8 輕量級鎖

2.8.1 slow_enter-獲取鎖

摘錄核心程式碼如下:

void ObjectSynchronizer::slow_enter(Handle obj, BasicLock* lock, TRAPS) {
  // 獲取物件頭的MarkWord部分
  markOop mark = obj->mark();
  // 此時不應該是偏向鎖模式
  assert(!mark->has_bias_pattern(), "should not see bias pattern here");

  if (mark->is_neutral()) {
  // is_neutral代表無鎖
    // Anticipate successful CAS -- the ST of the displaced mark must
    // be visible <= the ST performed by the CAS.
    // lock儲存該物件頭的MarkWord
    lock->set_displaced_header(mark);
    // CAS方式將目標物件的MarkWord替換為lock
    if (mark == (markOop) Atomic::cmpxchg_ptr(lock, obj()->mark_addr(), mark)) {
    // CAS方式成功,代表該執行緒成功獲取鎖,可以返回執行同步塊程式碼了
      TEVENT (slow_enter: release stacklock) ;
      return ;
    }
    // Fall through to inflate() ...
  } else
  if (mark->has_locker() && THREAD->is_lock_owned((address)mark->locker())) {
  // 此時MarkWord為輕量級鎖狀態,且該鎖擁有者就是當前Thread
    // 此時就是輕量級鎖的重入情況,可以返回了
    
    lock->set_displaced_header(NULL);
    return;
  }

// 搜了下,下面這段程式碼因為#if 0的,相當於註釋永不執行。。。
#if 0
  // The following optimization isn't particularly useful.
  if (mark->has_monitor() && mark->monitor()->is_entered(THREAD)) {
    lock->set_displaced_header (NULL) ;
    return ;
  }
#endif

  // The object header will never be displaced to this lock,
  // so it does not matter what the value is, except that it
  // must be non-zero to avoid looking like a re-entrant lock,
  // and must not look locked either.
  lock->set_displaced_header(markOopDesc::unused_mark());
  // 到了這裡,說明輕量級鎖存在競爭,需要膨脹為重量級鎖
  ObjectSynchronizer::inflate(THREAD, obj())->enter(THREAD);
} 

2.8.2 slow_exit-釋放鎖1

void ObjectSynchronizer::slow_exit(oop object, BasicLock* lock, TRAPS) {
  fast_exit (object, lock, THREAD) ;
}

2.8.3 fast_exit-釋放鎖2

摘錄核心程式碼如下:

void ObjectSynchronizer::fast_exit(oop object, BasicLock* lock, TRAPS) {
    // dhw指向MarkWord副本
    markOop dhw = lock->displaced_header();
    markOop mark ;
    // mark指向真實MarkWord
    mark = object->mark() ;
    if (mark == (markOop) lock) {
     assert (dhw->is_neutral(), "invariant") ;
     // 如果當前執行緒擁有鎖,就CAS方式還原MarkWord
     if ((markOop) Atomic::cmpxchg_ptr (dhw, object->mark_addr(), mark) == mark) {
        TEVENT (fast_exit: release stacklock) ;
        return;
     }
  }
  // CAS失敗,說明其他執行緒嘗試過獲取該鎖。
  // 此時不僅要釋放鎖,還需要膨脹為重量級鎖
  ObjectSynchronizer::inflate(THREAD, object)->exit (true, THREAD) ;
}

2.9 重量級鎖-膨脹

實現主要在以下方法中:

ObjectMonitor * ATTR ObjectSynchronizer::inflate (Thread * Self, oop object) 

關於重量級鎖的膨脹、鎖enter/exit等細節待補充,可以參見佔小狼的JVM原始碼分析之synchronized實現

2.10 monitorexit原始碼分析

2.10.1 InterpreterRuntime::monitorexit

核心程式碼如下:

IRT_ENTRY_NO_ASYNC
            
           

相關推薦

Java-併發--synchronized

Java-併發-鎖-synchronized 摘要 本文會詳細說下synchronized的底層實現原理。 0x01 基本概念 每次只能有一個執行緒進入臨界區 保證臨界區內共享變數的可見性和有序性 成功進入synchronized區域的執行緒可以拿到物

Java併發synchronized之偏向和輕量級

synchronized之偏向鎖和輕量級鎖 上下文切換 synchronized 鎖的升級與對比 偏向鎖 輕量級鎖 參考 上下文切換 即使是單核處理器也支援多執行緒執行程式碼執行程式碼,CPU通

JAVA併發 - 悲觀VS樂觀(一)

文章目錄 悲觀鎖VS樂觀鎖 1.悲觀鎖 1.1什麼是悲觀鎖 1.2原始碼分析 synchronized Lock 1.3應用場景 1.4實現

java併發之----synchronized與ReenTrantLock

Java 提供了兩種鎖機制來控制多個執行緒對共享資源的互斥訪問,第一個是 JVM 實現的 synchronized,而另一個是 JDK 實現的 ReentrantLock。 synchronized synchronized關鍵字最主要幾種使用方式: (1)同步一個程式碼塊: 只作用

Java-併發--LockSupport

Java-併發-鎖-LockSupport 0x01 摘要 LockSupport是用來建立鎖和其他同步類的基本執行緒阻塞原語,他的兩個主要方法park()和 unpark()的作用分別是阻塞執行緒和解除阻塞執行緒。本文簡要分析下他的原始碼。 0x02 原始碼解析 2.1

深入理解Java併發synchronized實現原理

關聯文章: 本篇主要是對Java併發中synchronized關鍵字進行較為深入的探索,這些知識點結合博主對synchronized的個人理解以及相關的書籍的講解(在結尾參考資料),如有誤處,歡迎留言。 執行緒安全是併發程式

JAVA互斥(synchronized&Lock):行為分析及原始碼

JVM中有這樣一段註釋: // The base-class, PlatformEvent, is platform-specific while the ParkEvent is // platform-independent. PlatformEvent provides park()

Java併發——框架(三)讀寫

1. 讀寫鎖機制——ReadWriteLock介面 讀寫鎖適用於對資料結構頻繁讀而較少修改的場景。舉個栗子,你可以建立一個線上詞典供多條讀執行緒併發讀取,然而單條寫執行緒可能會不時新增新的定義或更新已有的定義。一個資源可以被多個執行緒同時讀,或者被一個執行緒寫,但是不能同時存在讀和寫執行緒。&n

Java併發——框架(二)重入ReentrantLock

1. 重入鎖 重入鎖,顧名思義,就是支援重進入的鎖,它表示該鎖能夠支援一個執行緒對資源的重複加鎖。重進入是指任意執行緒在獲取到鎖之後能夠再次獲取該鎖而不會被鎖阻塞。兩個關鍵問題: (1)執行緒再次獲取鎖。當一條執行緒持有這個鎖並且呼叫lock()、lockUninterruptibly()或

Java併發——框架(一)

來源:《Java執行緒與併發程式設計實踐》以及          https://blog.csdn.net/qq_38293564/article/details/80476659 1. 鎖框架 java.util.co

(七)java併發程式設計synchronized+volatile(安全初始化模式例項)

安全釋出物件的安全性都來自於JMM提供的保證,而造成不正確的釋出原因,就是在”釋出一個共享物件”與”另一個執行緒訪問該物件”之間缺少一種Happen-Before排序. 不安全的釋出 package safe_unsafe_public

(六)java併發程式設計--synchronized同步塊

雖然前面文章的一些例項中已經使用synchronized關鍵字來實現執行緒的同步,但還是需要好好的理解一下。 一段程式碼或者一個方法被synchronized關鍵字標記我們就說這斷程式碼是同步塊。同步塊可以用來避免競爭條件。 synchronize

[Java]同步synchronized

在多執行緒操作資料時需要考慮使用synchronized關鍵詞修飾屬性或方法,同步鎖synchronized大致可分為兩類:物件鎖、全域性鎖。在分析同步鎖程式碼時需要關心兩個問題:鎖的物件是誰、誰持有鎖。 ①物件鎖: 當一個執行緒訪問物件object的一個synchronized(thi

Java-併發--ReentrantLock

Java-併發-鎖-ReentrantLock 摘要 ReentrantLock是使用最廣的、最出名的AQS(AbstractQueuedSynchronizer)系列的可重入鎖。本文會分析他的lock, unlock等重要方法,還涉及公平/非公平概念對比,及對比synchron

java併發程式設計---synchronized關鍵字

 在併發程式設計中,多執行緒同時併發訪問的資源叫做臨界資源,當多個執行緒同時訪問物件並要求操作相同資源時,分割了原子操作就有可能出現數據的不一致或資料不完整的情況,為避免這種情況的發生,我們會採取同步機制,以確保在某一時刻,方法內只允許有一個執行緒。       採用syn

《大話設計模式》讀書筆記:單例模式與Java同步synchronized

單例模式,保證一個類僅有一個例項,並提供一個訪問它的全域性訪問點。在單例模式下,類本身負責儲存它的唯一例項,這個類必須保證沒有其他例項可以被建立,並且它可以提供一個訪問該例項的方法。單例模式的類中,構造方法(函式/體)被設為private,從而堵死了外部例項化該類的可能。同

Java併發控制synchronized與AtomicInteger類

眾所周知,在Java多執行緒程式設計中,一個非常重要的方面就是執行緒的同步問題。關於執行緒的同步,最常用的解決方法就是使用synchronized關鍵字,但是如果使用場景只用在控制一個計數的整型變數時

原始碼閱讀:Java併發synchronized實現原理

執行緒安全是併發程式設計中的重要關注點,應該注意到的是,造成執行緒安全問題的主要誘因有兩點,一是存在共享資料(也稱臨界資源),二是存在多條執行緒共同操作共享資料。因此為了解決這個問題,我們可能需要這樣一個方案,當存在多個執行緒操作共享資料時,需要保證同一時刻有且

深入理解 Java 併發synchronized 實現原理

關聯文章深入理解Java型別資訊(Class物件)與反射機制深入理解Java列舉型別(enum)深入理解Java註解型別(@Annotation)深入理解Java併發之synchronized實現原理本篇主要是對Java併發中synchronized關鍵字進行較為深入的探索,這些知識點結合博主對synchro

java 併發synchronized 實現原理

在 java 開發中 synchronized 是使用的最多的工具。 表現形式 在 java 中每個物件都可以作為鎖: 對於普通同步方法,鎖是當前例項物件; 對於靜態同步方法,鎖是當前類的 Class 物件; 對於同步方法快,鎖是 Synchronized 括