JAVA基礎 - synchronized

看下面文字可以先看一下

JAVA基礎-volatile
瞭解一下 什麼是可見性。

目錄

1. synchronized 特點

	 1.1 可重入性
	 1.2 不可中斷性

2. synchronized 原理

3. monitor 原理

4. 鎖的分析

	4.0  前置知識
	4.1 偏向鎖
	4.2 輕量級鎖
	4.3 重量級鎖

5. synchronized


正文

1. synchronized 特點

1.1 可重入性

描述: 一個線程可以多次執行synchronized,重複獲取同一把鎖。
同一個線程獲得鎖之後,可以直接再次獲取該鎖。
原理:synchronize鎖對象中有計數器 recursions 變量。會記錄線程獲得幾次鎖。


public static void main(String[] args) {
        Runnable sellTicket = new Runnable() {
            @Override
		public void run() {
		synchronized (Demo01.class) {
		System.out.println("我是run");
		test01(); }
		}
public void test01() { synchronized (Demo01.class) {
			System.out.println("我是test01"); }
		} 
	};
		new Thread(sellTicket).start();
		new Thread(sellTicket).start(); }

1.2 不可中斷性?

描述:一個線程獲得鎖後,另外一個線程想要獲得鎖,必須處於阻塞或等待狀態,如果第一個線程不釋放鎖,第二線程會一直阻塞或等待,不可中斷。

private static Object object = new Object();
    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {

                synchronized (object) {
                    String name = Thread.currentThread().getName();
                    System.out.println(name + "進入同步代碼塊");
                    try {
                        Thread.sleep(10000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        Thread t1 = new Thread(runnable);
        t1.start();
        Thread.sleep(1000);
        Thread t2 = new Thread(runnable);
        t2.start();
        System.out.println("停止線程前");
        t2.interrupt();
        System.out.println("停止線程後");
        System.out.println(t1.getState());
        System.out.println(t2.getState());

    }
//輸出:
// Thread-0進入同步代碼塊
// 停止線程前
// 停止線程後
// TIMED_WAITING
// BLOCKED

難道真的沒有辦法 可以控制了嗎? 可以控制的。

    private static Lock lock = new ReentrantLock();
    private static Object object = new Object();
    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                String name = Thread.currentThread().getName();
                boolean flag = false;
                try {
                    flag = lock.tryLock(3, TimeUnit.SECONDS);
                    if (flag) {
                        System.out.println(name + "獲得鎖");
                        Thread.sleep(4888);
                    } else {
                        System.out.println("沒有獲得鎖");
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    if (flag) {
                        lock.unlock();
                    }
                    System.out.println("釋放鎖");
                }
            }
        };
        Thread t1 = new Thread(runnable);
        t1.start();
        Thread.sleep(1000);
        Thread t2 = new Thread(runnable);
        t2.start();
    }
//  輸出
// Thread-0獲得鎖
// 沒有獲得鎖
// 釋放鎖
// 釋放鎖

總結lock和synchronized:

  1. synchronized是關鍵字,而 lock是一個接口。
  2. synchronize會自動釋放鎖,而lock 必須手動釋放鎖。
  3. synchronize是不可中斷的,Lock可以中斷也可以不中斷。
  4. 通過Lock可以知道線程有沒有拿到鎖,而synchronize不能
  5. synchronize能鎖住方法和代碼塊,而lock只能鎖住代碼塊
  6. Lock可以使用讀鎖提高多線程讀效率
  7. synchronize是非公平鎖,ReentrantLock可以控制是否公平鎖。

3.monitor 原理

一下代碼 先反編譯看:

// Test07Visibility.java
 //  執行 javap -p -v -c Test07Visibility.class  
  	private static  Object obj=new Object();
    public static  void main(String [] args){
        synchronized (obj){
            System.out.println(" 鎖1  ");
        }
    }
    public synchronized  void test(){
        System.out.println(" 鎖2  ");
    }

java 編譯
monitorenter 紅框1 JVM 規範中寫到:

https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.monitorenter

翻譯一下:
說明: 每一個對象都會和一個監視器monitor關聯,監視器被佔用時候會被鎖住,其他線程無法來
獲取該monitor,當jvm執行某個現場的某個方法內部的monitorenter時,它會嘗試去獲取當前對象
對應的monitor的所有權,其過程如下:

  1. 若monitor的進入數爲0,線程可以進入monitor,並將monitor的進入數值爲1,當前線程成爲monitor的owner(所有者)
  2. 若線程已經擁有monitor的所有權,允許它重入monitor,則進入monitor的進入數加1
  3. 若其他線程已經佔有monitor的所有權,那麼當前嘗試monitor的所有權的線程會被阻塞,直到monitor的進入數變爲0,才能 重新嘗試獲取monitor的所有權。

monitorenter小結:
synchronized的鎖對象會關聯一個monitor,這個monitor不是我們主動創建的,是jvm的線程執行到這個同步代碼塊
發現鎖對象沒有monitor就會創建monitor,monitor內部有兩個重要的成員變量owner,擁有這把所的線程,
recursions會記錄線程擁有鎖的次數,當一個線程擁有monitor後其他線程只能等待。

monitorenter 紅框2 JVM 規範中寫到:
https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.monitorexit

翻譯一下:
1.能執行monitore exit 指令的線程一定是擁有當前對象的monitor的所有權的線程。
2.執行monitorexitexit時會將monitor的進入數減一,當monitor的進入數減爲0時,當前線程退出monitor
,不在擁有monitor的所有權,此時其他被這個monitor阻塞的線程可以嘗試去獲取這個monitor得所有權。

monitorexit 釋放鎖
monitorexit 插入在方法結束處和異常處,jvm保證每個monitorenter必須有對應的monitoreexit。
可以參考 紅框3

面試題synchroznied出現異常會釋放鎖嗎?
答: 會釋放鎖.

monitor 存儲結構

在HotSpot虛擬機忠, monitor 是由ObjectMonitor實現的,其源碼是用C++來實現的,
位於HotSpot虛擬機源碼 ObjectMonitor.hpp 文件

ObjectMonitor() {
 *     _header       = NULL;
 *     _count        = 0;
 *     _waiters      = 0,
 *     _recursions   = 0; // 線程的重入次數
 *     _object       = NULL; // 存儲該monitor的對象
 *     _owner        = NULL; //標識擁有該 monitor的線程
 *     _WaitSet      = NULL; // 處於wait 狀態的線程,會被加入到——waitSet 中
 *     _WaitSetLock  = 0 ;
 *     _Responsible  = NULL ;
 *     _succ         = NULL ;
 *     _cxq          = NULL ;// 多線程競爭鎖時的單向列表
 *     FreeNext      = NULL ;
 *     _EntryList    = NULL ;// 處於等待鎖 lock狀態的線程,會被加入到該列表
 *     _SpinFreq     = 0 ;
 *     _SpinClock    = 0 ;
 *     OwnerIsThread = 0 ;
 *     _previous_owner_tid = 0;
 *   }

描述:

  1. _owner:初始化爲NULL,當前線程佔有該monitor時,owner標記爲該線程的唯一標識。
    當線程釋放monitor時,owner又恢復爲Null,owner是一個臨界資源,jvm是通過CAS操作來保證其線程安全帶。
  2. _cxq:競爭隊列,所有請求鎖的線程首先會被這個隊列中 (單向鏈接),_cxq是一個臨界資源,jvm
    通過CAS原子指令來修改_cxq隊列。修改前_cxq的舊值填入node的next字段,_cxq指向新值(新線程),因此cxq是一個
    後進先出的stack(棧).
  3. entryList: cxq 隊列中有資格成爲候選資源的線程會被移動到該隊列中。
  4. WaitSet: 因爲調用wait方法而被阻塞的線程會被放在該隊列中。

每一個java 對象都可以與一個監視器monitor 關聯,我們可以把它理解爲一把鎖,當一個線程想要執行一段被synchronize圈起來的同步方法或者代碼塊時,該線程得先獲取到synchronize修飾的對象對應的monitor

我們得java代碼裏不會顯示地區創造這麼一個monitor對象,我們也無需創建,事實上可以這麼理解:

monitor並不是隨着對象創建而創建的。我們是通過synchronized修改事符告訴jvm需要爲我們得某個對象
創建關聯monitor對象

。每個線程都存在兩個ObjectMonitor對象列表。分別爲free和used列表
同時JVM也在維護着global lockList。當線程需要ObjectMonitor對象時,首先從線程自身的free表中申請,若存在則使用,
若不存在則從global list中申請。

在這裏插入圖片描述

分析 monitorenter

interpreterRuntime.cpp

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");
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");
  1. 對於重量級鎖,monitorenter函數中會調用 ObjectSynchronizer::slow_enter(h_obj, elem->lock(), CHECK);
  2. 最終調用ObjectMonitor:enter (objectMonitor.cpp)
objectMonitor.cpp
void ATTR ObjectMonitor::enter(TRAPS) {
// The following code is ordered to check the most common cases first
// and to reduce RTS->RTO cache line upgrades on SPARC and IA32 processors. Thread * const Self = THREAD ;
	void * cur ;
	// 通過CAS操作嘗試把monitor的_owner字段設置爲當前線程 
	cur = Atomic::cmpxchg_ptr (Self, &_owner, NULL) ; 
	if (cur == NULL) {
	// Either ASSERT _recursions == 0 or explicitly set _recursions = 0.
		assert (_recursions == 0"invariant") ; 
		assert (_owner == Self, "invariant") ;
 	// CONSIDER: set or assert OwnerIsThread == 1 return ;
}
	// 線程重入,
	recursions++
    if (cur == Self) {
	// TODO-FIXME: check for integer overflow! BUGID 6557169.
     _recursions ++ ;
	return ; 
}
// 如果當前線程是第一次進入該monitor,設置_recursions爲1,_owner爲當前線程
	if (Self->is_lock_owned ((address)cur)) {
		assert (_recursions == 0"internal state error");
		_recursions = 1 ;
// Commute owner from a thread-specific on-stack BasicLockObject address to // a full-fledged "Thread *".
		_owner = Self ;
		OwnerIsThread = 1 ;
		return ;
}
// 省略一些代碼 
	for (;;) {
		jt->set_suspend_equivalent();
// cleared by handle_special_suspend_equivalent_condition() // or java_suspend_self()
// 如果獲取鎖失敗,則等待鎖的釋放; 
	EnterI (THREAD) ;
    if (!ExitSuspendEquivalent(jt)) break ;
//
// We have acquired the contended monitor, but while we were
// waiting another thread suspended us. We don't want to enter 
// the monitor while suspended because that would surprise the
// thread that suspended us. //
    _recursions = 0 ;
  	_succ = NULL ;
	exit (false, Self) ; 
	jt->java_suspend_self();
}
Self->set_current_pending_monitor(NULL); }

描述:
1.通過CAS嘗試把monitor的owner字段設置爲當前線程。
2. 如果設置之前的owner指向當前線程,說明當前線程再次進入monitor,即重入鎖,
執行recursions++,記錄重入的次數
3.如果當前線程時是一次進入該monitor,設置recursions爲1,_owner爲當前線程。該線程成功獲得鎖並返回
4.如果獲取鎖失敗,則等待鎖的釋放。

分析 EnterI

void ATTR ObjectMonitor::EnterI (TRAPS) {
    Thread * Self = THREAD ;
 // Try the lock - TATAS
if (TryLock (Self) > 0) {
    assert (_succ != Self, "invariant") ;
    assert (_owner == Self, "invariant") ;
    assert (_Responsible != Self, "invariant") ;
    return ;
}
if (TrySpin (Self) > 0) {
    assert (_owner == Self , "invariant") ;
    assert (_succ != Self , "invariant") ;
    assert (_Responsible != Self  , "invariant") ;
    return ;
}
// 省略部分代碼
// 當前線程被封裝成ObjectWaiter對象node,狀態設置成ObjectWaiter::TS_CXQ; ObjectWaiter node(Self) ;
	Self->_ParkEvent->reset() ;
	node._prev = (ObjectWaiter *) 0xBAD ;
	node.TState = ObjectWaiter::TS_CXQ ;
// 通過CAS把node節點push到_cxq列表中 
	ObjectWaiter * nxt ;
	for (;;) {
		node._next = nxt = _cxq ;
	if (Atomic::cmpxchg_ptr (&node, &_cxq, nxt) == nxt) break ;
	// Interference - the CAS failed because _cxq changed. Just retry.
    // As an optional optimization we
    if (TryLock (Self) > 0) {
        assert (_succ != Self, "invariant") ;
        assert (_owner == Self, "invariant") ; 
        assert (_Responsible != Self, "invariant") ;
        return ;
	} 
}
// 省略部分代碼 
	for (;;) { 
// 線程在被掛起前做一下掙扎,看能不能獲取到鎖 
		if (TryLock (Self) > 0) break ;
		assert (_owner != Self, "invariant") ;

		if ((SyncFlags & 2) && _Responsible == NULL) {
		 Atomic::cmpxchg_ptr (Self, &_Responsible, NULL) ;
	}
// park self
if (_Responsible == Self || (SyncFlags & 1)) {
	TEVENT (Inflated enter - park TIMED) ;
 	Self->_ParkEvent->park ((jlong) RecheckInterval) ;
		// Increase the RecheckInterval, but clamp the value. 
	RecheckInterval *= 8 ;
    if (RecheckInterval > 1000) RecheckInterval = 1000 ;
} else {
	TEVENT (Inflated enter - park UNTIMED) ;
	// 通過park將當前線程掛起,等待被喚醒
	Self->_ParkEvent->park() ; 
}
    if (TryLock(Self) > 0) break ;
// 省略部分代碼
 }
// 省略部分代碼
 }   

說明:
當線程被喚醒時,會從掛起的點繼續執行,通過objectMoniter::TryLock嘗試獲取鎖,
TryLock 方法實現如下:

int ObjectMonitor::TryLock (Thread * Self) {
   for (;;) {
		void * own = _owner ;
		if (own != NULL) return 0 ;
		if (Atomic::cmpxchg_ptr (Self, &_owner, NULL) == NULL) {
			// Either guarantee _recursions == 0 or set _recursions = 0.
			assert (_recursions == 0"invariant") ;
			assert (_owner == Self, "invariant") ;
			// CONSIDER: set or assert that OwnerIsThread == 1
			 return 1 ;
}
// The lock had been free momentarily, but we lost the race to the lock.
// Interference -- the CAS failed.
// We can either return -1 or retry.
// Retry doesn't make as much sense because the lock was just acquired.
		if (true) return -1 ;
	}
 }

以上代碼流程:
1.當前線程被封裝成objectWaiter對象node,狀態設置成爲ObjectWaiter::TS_CXQ.
2在for循環中,通過CAS把node結點push到_cxq列表中,同一時刻可能有多個線程把自己的node節點
push到_cxq列表中。
3.node節點push到_cxq列表之後,通過自旋嘗試獲取鎖,如果還是沒有獲取鎖,則通過park將
當前線程掛起,等待被喚醒。
4.當該線程被喚醒時,會從掛起的點繼續執行,通過ObjectMonitor::TryLock 嘗試獲取鎖

monitor釋放

當某個持有鎖的線程執行完同步代碼塊,會進行鎖的釋放。給其他線程機會執行同步代碼,
在hotSpot中,通過退出monitor的方式實現鎖的釋放,並通知被阻塞的線程。
具體實現位於ObjectMonitor的exit方法中

objectMonitor.cpp
oid ATTR ObjectMonitor::exit(bool not_suspended, TRAPS) {
	 Thread * Self = THREAD ;
// 省略部分代碼
	if (_recursions != 0) {
	_recursions--; // this is simple recursive enter
	TEVENT (Inflated exit - recursive) ;
	return ;
 }

// 省略部分代碼 
	ObjectWaiter * w = NULL ;
	int QMode = Knob_QMode ;
	
// qmode = 2:直接繞過EntryList隊列,從cxq隊列中獲取線程用於競爭鎖
	if (QMode == 2 && _cxq != NULL) {
		w = _cxq ;
		assert (w != NULL"invariant") ;
		assert (w->TState == ObjectWaiter::TS_CXQ, "Invariant") ;
		ExitEpilog (Self, w) ;
		return ;
}

// qmode =3:cxq隊列插入EntryList尾部;
if (QMode == 3 && _cxq != NULL) {
     w = _cxq ;
     for (;;) {
		assert (w != NULL"Invariant") ;
		ObjectWaiter * u = (ObjectWaiter *) Atomic::cmpxchg_ptr (NULL,, "invariant") ;
	&_cxq, w) ; 
    if (u == w) break ;
    w=u;
}
	assert (w != NULL"Invariant") ;
	ObjectWaiter * q = NULL ;
	ObjectWaiter * p ;
for (p = w ; p != NULL ; p = p->_next) {
 	 guarantee (p->TState == ObjectWaiter::TS_CXQ, "Invariant") ;
	 p->TState = ObjectWaiter::TS_ENTER ;
	 p->_prev = q ;
	 q=p;
}
     ObjectWaiter * Tail ;
	for (Tail = _EntryList ; Tail != NULL && Tail->_next != NULL ; Tail = Tail->_next) ;
     if (Tail == NULL) {
            _EntryList = w ;
 	} else {
	Tail->_next = w ;
	 w->_prev = Tail ;
 } 
}

// qmode =4:cxq隊列插入到_EntryList頭部 
if (QMode == 4 && _cxq != NULL) {
         w = _cxq ;
         for (;;) {
			assert (w != NULL"Invariant") ;
			ObjectWaiter * u = (ObjectWaiter *) Atomic::cmpxchg_ptr (NULL&_cxq, w) ;
		if(u == w) break;
		 w=u;
}
assert (w != NULL"Invariant") ;

	ObjectWaiter * q = NULL ;
	ObjectWaiter * p ;
	for (p = w ; p != NULL ; p = p->_next) {
		guarantee (p->TState == ObjectWaiter::TS_CXQ, "Invariant") ; 
		p->TState = ObjectWaiter::TS_ENTER ;
		p->_prev = q ;
		q=p;
	}

	if (_EntryList != NULL) { 
		q->_next = _EntryList ;
		 _EntryList->_prev = q ;
	}
    _EntryList = w ;
}
	w = _EntryList  ;
	if (w != NULL) {
		assert (w->TState == ObjectWaiter::TS_ENTER, "invariant") ;
		ExitEpilog (Self, w) ;
		return ;
	}
	w = _cxq ;
	if (w == NULL) continue ;

	for (;;) {
		assert (w != NULL"Invariant") ;
		ObjectWaiter * u = (ObjectWaiter *) Atomic::cmpxchg_ptr (NULL&_cxq,
 	   if (u == w) break ;
		w=u; 
}
	TEVENT (Inflated exit - drain cxq into EntryList) ;
	assert (w != NULL"invariant") ;
	assert (_EntryList == NULL"invariant") ;
}

	ObjectWaiter * q = NULL ;
	ObjectWaiter * p ;
	for (p = w ; p != NULL ; p = p->_next) {
		guarantee (p->TState == ObjectWaiter::TS_CXQ, "Invariant") ;
	 	p->TState = ObjectWaiter::TS_ENTER ;
		p->_prev = q ;
		q=p;
}		

	if (_EntryList != NULL) { 
		q->_next = _EntryList ; _EntryList->_prev = q ;
	}
 	   _EntryList = w ;
}

	w = _EntryList  ;
	if (w != NULL) {
		assert (w->TState == ObjectWaiter::TS_ENTER, "invariant") ; ExitEpilog (Self, w) ;
		return ;
}
	w = _cxq ;
	if (w == NULL) continue ;

	for (;;) {
		assert (w != NULL"Invariant") ;
		ObjectWaiter * u = (ObjectWaiter *) Atomic::cmpxchg_ptr (NULL&_cxq,
 	   if (u == w) break ;
			w=u; 
	}

	TEVENT (Inflated exit - drain cxq into EntryList) ; 
	assert (w != NULL"invariant") ;
	assert (_EntryList == NULL"invariant") ;

	if (QMode == 1) {
		// QMode == 1 : drain cxq to EntryList, reversing order 
		// We also reverse the order of the list.
		ObjectWaiter * s = NULL ;
		ObjectWaiter * t = w ;
		ObjectWaiter * u = NULL ;
	while (t != NULL) {
		guarantee (t->TState == ObjectWaiter::TS_CXQ, "invariant") ; t->TState = ObjectWaiter::TS_ENTER ;
		u = t->_next ;
		t->_prev = u ;
		t->_next = s ; s = t;
		t=u;
}
	_EntryList = s ;
	assert (s != NULL"invariant") ;
	 } else {
		// QMode == 0 or QMode == 2
		_EntryList = w ;
		ObjectWaiter * q = NULL ;
		ObjectWaiter * p ;
	for (p = w ; p != NULL ; p = p->_next) {
		guarantee (p->TState == ObjectWaiter::TS_CXQ, "Invariant") ; p->TState = ObjectWaiter::TS_ENTER ;
		p->_prev = q ;
		q=p;
	}
 }
if (_succ != NULL) continue;
     w = _EntryList  ;
     if (w != NULL) {
		guarantee (w->TState == ObjectWaiter::TS_ENTER, "invariant") ; ExitEpilog (Self, w) ;
		return ;
	}
  }
}
 

1.退出同步代碼塊會讓_recursions減1,當_recursions的值減爲0時,說明線程釋放了鎖。
2.根據不同的策略(由QMode指定),從cxq或者EntryList獲取頭節點,通過objectMonitor""EXITepilog方法
喚醒該節點封裝線程,喚醒操作最終由unpark完成。實現如下:

void ObjectMonitor::ExitEpilog (Thread * Self, ObjectWaiter * Wakee) {
	 assert (_owner == Self, "invariant") ;
	_succ = Knob_SuccEnabled ? Wakee->_thread : NULL ;
	 ParkEvent * Trigger = Wakee->_event ;
	Wakee = NULL ;
   // Drop the lock
	OrderAccess::release_store_ptr (&_owner, NULL) ;
   OrderAccess::fence() ;
unpark()

   if (SafepointSynchronize::do_call_back()) {
      TEVENT (unpark before SAFEPOINT) ;
	// ST _owner vs LD in
}

DTRACE_MONITOR_PROBE(contended__exit, thisobject(), Self);
Trigger->unpark() ; // 喚醒之前被pack()掛起的線程.
   // Maintain stats and report events to JVMTI
	if (ObjectMonitor::_sync_Parks != NULL) { 
		ObjectMonitor::_sync_Parks->inc() ;
	} 
}

被喚醒的線程,會回到 EnterI 的600 行,繼續執行monitor的競爭

// park self
if (_Responsible == Self || (SyncFlags & 1)) {
	TEVENT (Inflated enter - park TIMED) ;
	Self->_ParkEvent->park ((jlong) RecheckInterval) ;
// Increase the RecheckInterval, but clamp the value. 
	RecheckInterval *= 8 ;
    if (RecheckInterval > 1000) RecheckInterval = 1000 ;
} else {
	TEVENT (Inflated enter - park UNTIMED) ;
	Self->_ParkEvent->park() ;
 }
if (TryLock(Self) > 0) break ;

monitor是重量級鎖

可以看到ObjectMonitor的函數調用中會涉及到Atomic::cmpxchg_ptr,Atomic::inc_ptr等內核函數, 執行同步代碼塊,沒有競爭到鎖的對象會park()被掛起,競爭到鎖的線程會unpark()喚醒。這個時候就 會存在操作系統用戶態和內核態的轉換,這種切換會消耗大量的系統資源。所以synchronized是Java語 言中是一個重量級(Heavyweight)的操作。
(這是未優化之前)

CSDN 篇幅寫不下
請看: JAVA基礎 - 鎖

參考

https://www.bilibili.com/video/av82315694
理解Java內存模型
你瞭解Java內存結構麼(Java7、8、9內存結構的區別)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章