synchronized
實現機制:反編譯以後monitor
優化:自旋鎖,適應自旋鎖,輕量級鎖等
所謂自旋鎖,就是讓該線程等待一段時間,不會被立即掛起,看持有鎖的線程是否會很快釋放鎖。怎麼等待呢?執行一段無意義的循環即可(自旋)。
雖然可以避免線程切換帶來的開銷,但是佔用處理器時間
volatile
原子性:一個操作或者多個操作 要麼全部執行並且執行的過程不會被任何因素打斷,要麼就都不執行。(volatile是無法保證複合操作的原子性)
可見性:當多個線程訪問同一個變量時,一個線程修改了這個變量的值,其他線程能夠立即看得到修改的值。(Java提供了volatile來保證可見性)
有序性:程序執行的順序按照代碼的先後順序執行。(Java提供volatile來保證一定的有序性)
使用它必須滿足如下兩個條件:
對變量的寫操作不依賴當前值;
該變量沒有包含在具有其他變量的不變式中。
volatile經常用於兩個場景:狀態標記、double check
ReentrantLock
AQS,AbstractQueuedSynchronizer,即隊列同步器。
AQS使用一個int類型的成員變量state來表示同步狀態,當state>0時表示已經獲取了鎖,當state = 0時表示釋放了鎖。它提供了三個方法(getState()、setState(int newState)、compareAndSetState(int expect,int update))來對同步狀態state進行操作,當然AQS可以確保對state的操作是安全的。
AQS通過內置的FIFO同步隊列來完成資源獲取線程的排隊工作,如果當前線程獲取同步狀態失敗(鎖)時,AQS則會將當前線程以及等待狀態等信息構造成一個節點(Node)並將其加入同步隊列,同時會阻塞當前線程,當同步狀態釋放時,則會把節點中的線程喚醒,使其再次嘗試獲取同步狀態。
該隊列就是CLH同步隊列。CLH同步隊列是一個FIFO雙向隊列,AQS依賴它來完成同步狀態的管理
private Node addWaiter(Node mode) {
Node node = new Node(mode);
for (;;) {
Node oldTail = tail;
if (oldTail != null) {
//尾節點不爲null就添加到尾節點的next(CAS)
U.putObject(node, Node.PREV, oldTail);
if (compareAndSetTail(oldTail, node)) {
oldTail.next = node;
return node;
}
} else {
//尾節是null就初始化整個隊列
initializeSyncQueue();
}
}
}
前面提到ReentrantLock提供了比synchronized更加靈活和強大的鎖機制,那麼它的靈活和強大之處在哪裏呢?他們之間又有什麼相異之處呢?
首先他們肯定具有相同的功能和內存語義。
與synchronized相比,ReentrantLock提供了更多,更加全面的功能,具備更強的擴展性。例如:時間鎖等候,可中斷鎖等候,鎖投票。
ReentrantLock還提供了條件Condition,對線程的等待、喚醒操作更加詳細和靈活,所以在多個條件變量和高度競爭鎖的地方,ReentrantLock更加適合(以後會闡述Condition)。
ReentrantLock提供了可輪詢的鎖請求。它會嘗試着去獲取鎖,如果成功則繼續,否則可以等到下次運行時處理,而synchronized則一旦進入鎖請求要麼成功要麼阻塞,所以相比synchronized而言,ReentrantLock會不容易產生死鎖些。
ReentrantLock支持更加靈活的同步代碼塊,但是使用synchronized時,只能在同一個synchronized塊結構中獲取和釋放。注:ReentrantLock的鎖釋放一定要在finally中處理,否則可能會產生嚴重的後果。
ReentrantLock支持中斷處理,且性能較synchronized會好些。
條件Condition說明(http://blog.csdn.net/chenssy/article/details/69279356)一個有頭結點和尾節點的單向隊列