- synchronize 修飾的同步代碼塊:使用monitorenter 和 monitorexit 指令實現;
- synchronize 修飾的方法並沒有 monitorenter 和 monitorexit 指令 ,而取代之的是 ACC_SYNCHRONIZED標識,該標志指明了該方法是一個同步方法,從而執行相應的同步調用。
(1)synchronized修飾的方法
public synchronized void method() { int i = 0; }
javap反編譯后:
public synchronized void method(); descriptor: ()V flags: ACC_PUBLIC, ACC_SYNCHRONIZED Code: stack=1, locals=2, args_size=1 0: iconst_0 1: istore_1 2: return LineNumberTable: line 6: 0 line 7: 2 LocalVariableTable: Start Length Slot Name Signature 0 3 0 this Lcom/lock/SyncTest; 2 1 1 i I
對於同步方法,JVM會講方法設置 ACC_SYNCHRONIZED 標志,調用的時候 JVM 根據這個標志判斷是否是同步方法。
(2)synchronized修改的代碼塊
public void method() { synchronized (this) { int i = 0; } }
javap反編譯后:
public void method(); descriptor: ()V flags: ACC_PUBLIC Code: stack=2, locals=3, args_size=1 0: aload_0 1: dup 2: astore_1 3: monitorenter 4: iconst_0 5: istore_2 6: aload_1 7: monitorexit 8: goto 14 11: aload_1 12: monitorexit 13: athrow 14: return Exception table: from to target type 4 8 11 any 11 13 11 any LineNumberTable: line 6: 0 line 7: 4 line 6: 6 line 9: 14 LocalVariableTable: Start Length Slot Name Signature 0 15 0 this Lcom/lock/SyncTest;
// Synchronization // // The interpreter's synchronization code is factored out so that it can // be shared by method invocation and synchronized blocks. //%note synchronization_3 //%note monitor_1 // 參數:當前線程、當前對象 IRT_ENTRY_NO_ASYNC(void, InterpreterRuntime::monitorenter(JavaThread* thread, BasicObjectLock* elem)) #ifdef ASSERT thread->last_frame().interpreter_frame_verify_monitor(elem); #endif if (PrintBiasedLockingStatistics) { Atomic::inc(BiasedLocking::slow_path_entry_count_addr()); } Handle h_obj(thread, elem->obj()); assert(Universe::heap()->is_in_reserved_or_null(h_obj()), "must be NULL or an object"); // 是否開啟偏向鎖功能,JVM參數:+UseBiasedLocking表示開啟偏向鎖 if (UseBiasedLocking) { // Retry fast entry if bias is revoked to avoid unnecessary inflation // 為了避免不必要的膨脹,如果偏向鎖已被取消,則快速重試 ObjectSynchronizer::fast_enter(h_obj, elem->lock(), true, CHECK); } else { ObjectSynchronizer::slow_enter(h_obj, elem->lock(), CHECK); } assert(Universe::heap()->is_in_reserved_or_null(elem->obj()), "must be NULL or an object"); #ifdef ASSERT thread->last_frame().interpreter_frame_verify_monitor(elem); #endif IRT_END
// ---------------------------------偏向鎖的獲取-------------------------------------------- // Fast Monitor Enter/Exit // This the fast monitor enter. The interpreter and compiler use // some assembly copies of this code. Make sure update those code // if the following function is changed. The implementation is // extremely sensitive to race condition. Be careful. void ObjectSynchronizer::fast_enter(Handle obj, BasicLock* lock, bool attempt_rebias, TRAPS) { // 如果開啟了偏向鎖功能 if (UseBiasedLocking) { // 不在安全點 if (!SafepointSynchronize::is_at_safepoint()) { // 撤銷偏向鎖 BiasedLocking::Condition cond = BiasedLocking::revoke_and_rebias(obj, attempt_rebias, THREAD); if (cond == BiasedLocking::BIAS_REVOKED_AND_REBIASED) { return; } } else { // 判斷是否能重偏向 assert(!attempt_rebias, "can not rebias toward VM thread"); BiasedLocking::revoke_at_safepoint(obj); } // 對象頭是否有偏向標記,如果沒有提示偏向鎖將被取消,執行slow_enter assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now"); } // slow_enter (obj, lock, THREAD) ; } // --------------------------------輕量級鎖的獲取--------------------------------------------- // Interpreter/Compiler Slow Case // This routine is used to handle interpreter/compiler slow case // We don't need to use fast path here, because it must have been // failed in the interpreter/compiler code. void ObjectSynchronizer::slow_enter(Handle obj, BasicLock* lock, TRAPS) { // 對象頭 markOop mark = obj->mark(); assert(!mark->has_bias_pattern(), "should not see bias pattern here"); // 判斷是否為無鎖狀態 if (mark->is_neutral()) { // Anticipate successful CAS -- the ST of the displaced mark must // be visible <= the ST performed by the CAS. // 鎖指向該對象頭 lock->set_displaced_header(mark); // CAS加鎖操作,如果成功則結束,否則繼續執行后續代碼... if (mark == (markOop) Atomic::cmpxchg_ptr(lock, obj()->mark_addr(), mark)) { TEVENT (slow_enter: release stacklock) ; return ; } // Fall through to inflate() ... // 如果是輕量級鎖,並且該鎖被當前線程持有(上面已經判斷過是否可偏向,所以這里直接跳過偏向鎖的判斷) } else if (mark->has_locker() && THREAD->is_lock_owned((address)mark->locker())) { assert(lock != mark->locker(), "must not re-lock the same lock"); assert(lock != (BasicLock*)obj->mark(), "don't relock with same BasicLock"); // 鎖指向null lock->set_displaced_header(NULL); return; } #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); }
ObjectMonitor() { _header = NULL; _count = 0; // 記錄個數 _waiters = 0, _recursions = 0; _object = NULL; _owner = NULL; // 記錄當前持有鎖的線程ID _WaitSet = NULL; // 等待池:處於wait狀態的線程,會被加入到_WaitSet _WaitSetLock = 0 ; _Responsible = NULL ; _succ = NULL ; _cxq = NULL ; FreeNext = NULL ; _EntryList = NULL ; // 鎖池:處於等待鎖block狀態的線程,會被加入到該列表 _SpinFreq = 0 ; _SpinClock = 0 ; OwnerIsThread = 0 ; }
ObjectMonitor 中有兩個隊列,_WaitSet 和 _EntryList ,用來保存ObjectWaiter 對象列表,每個等待鎖的線程都會被封裝成ObjectWaiter 對象,_owner 指向持有ObjectMonitor 對象的線程,當多個線程同時訪問同一同步代碼塊或者同步方法時,首先會進入 _EntryList 隊列,當線程獲取到monitor 后進入_Owner 區域並把 monitor中的 _Owner 變量設置為當前線程,同時monitor 中的計數器count 加1,若線程調用wait() 方法,將釋放當前持有的monitor,_owner變量恢復為null,count 自減 1 ,同時該線程進入_WaitSet 集合中等待被喚醒。若當前線程執行完畢也將釋放monitor(鎖)並復位變量的值,以便其他線程進入獲取monitor(鎖)。如下圖所示:
- _owner 記錄當前持有鎖的線程ID
- _EntryList 是一個隊列,記錄所有阻塞等待鎖的線程(阻塞隊列,鎖池)
- _WaitSet 也是一個隊列,記錄調用 wait() 方法並還未被通知的線程(等待池)
// ----------------------------------------------------------------------------- // Wait/Notify/NotifyAll // NOTE: must use heavy weight monitor to handle wait() void ObjectSynchronizer::wait(Handle obj, jlong millis, TRAPS) { // 是否開啟偏向鎖 if (UseBiasedLocking) { // 撤銷偏向鎖 BiasedLocking::revoke_and_rebias(obj, false, THREAD); assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now"); } if (millis < 0) { TEVENT (wait - throw IAX) ; THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "timeout value is negative"); } // 鎖膨脹(升級為重量級鎖) ObjectMonitor* monitor = ObjectSynchronizer::inflate(THREAD, obj()); DTRACE_MONITOR_WAIT_PROBE(monitor, obj(), THREAD, millis); // 調用ObjectMonitor的wait方法 monitor->wait(millis, true, THREAD); /* This dummy call is in place to get around dtrace bug 6254741. Once that's fixed we can uncomment the following line and remove the call */ // DTRACE_MONITOR_PROBE(waited, monitor, obj(), THREAD); dtrace_waited_probe(monitor, obj, THREAD); } void ObjectSynchronizer::notify(Handle obj, TRAPS) { if (UseBiasedLocking) { // 撤銷偏向鎖 BiasedLocking::revoke_and_rebias(obj, false, THREAD); assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now"); } markOop mark = obj->mark(); // 如果是輕量級鎖,並且當前線程擁有該鎖,直接返回 if (mark->has_locker() && THREAD->is_lock_owned((address)mark->locker())) { return; } // 鎖膨脹(升級為重量級鎖),並調用ObjectMonitor的notify方法 ObjectSynchronizer::inflate(THREAD, obj())->notify(THREAD); } // NOTE: see comment of notify() void ObjectSynchronizer::notifyall(Handle obj, TRAPS) { if (UseBiasedLocking) { // 撤銷偏向鎖 BiasedLocking::revoke_and_rebias(obj, false, THREAD); assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now"); } markOop mark = obj->mark(); // 如果是輕量級鎖,並且當前線程擁有該鎖,直接返回 if (mark->has_locker() && THREAD->is_lock_owned((address)mark->locker())) { return; } // 鎖膨脹(升級為重量級鎖),並調用ObjectMonitor的notifyAll方法 ObjectSynchronizer::inflate(THREAD, obj())->notifyAll(THREAD); }
public class AccountingSync implements Runnable{ static AccountingSync instance=new AccountingSync(); static int i=0; static int j=0; @Override public void run() { for(int j=0;j<1000000;j++){ //this,當前實例對象鎖 synchronized(this){ i++; increase();//synchronized的可重入性 } } } public synchronized void increase(){ j++; } public static void main(String[] args) throws InterruptedException { Thread t1=new Thread(instance); Thread t2=new Thread(instance); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println(i); } }
正如代碼所演示的,在獲取當前實例對象鎖后進入synchronized代碼塊執行同步代碼,並在代碼塊中調用了當前實例對象的另外一個synchronized方法,再次請求當前實例鎖時,將被允許,進而執行方法體代碼,這就是重入鎖最直接的體現,需要特別注意另外一種情況,當子類繼承父類時,子類也是可以通過可重入鎖調用父類的同步方法。注意由於synchronized是基於monitor實現的,因此每次重入,monitor中的計數器仍會加1。