Java基礎——同步與鎖

Java基礎——同步與鎖

volatile

特色:html

  1. 提供一種輕量級的同步機制,沒有線程調度和上下文切換的開銷。
  2. 保證變量在多個線程之間的可見性(共享),強制變量的讀寫操做都在主存上進行,而不是在寄存器或CPU緩存上,使變量的修改能夠被全部線程看到。
  3. volatile變量不保證原子性,所以,同步操做仍是須要經過鎖機制來實現。

不適用的場景:java

場景一:變量用於非原子的操做編程

private volatile int count;

public void increment() {
    count++;
}

++操做不是原子操做,volatile不能保證數據的原子性。若是須要原子的讀寫操做,可使用concurrent包裏的AtomicXxx類。緩存

場景二:變量與其它變量在一個不變式條件中(以下:lower < upper是一個不變式)多線程

public class NumberRange {
	private int lower, upper;

	public int getLower() { return lower; }
	public int getUpper() { return upper; }

	public void setLower(int value) { 
    	if (value > upper) 
        	throw new IllegalArgumentException(...);
    	lower = value;
	}

	public void setUpper(int value) { 
    	if (value < lower) 
        	throw new IllegalArgumentException(...);
    	upper = value;
	}
}

多線程狀況下,若是兩個線程同時修改lower和upper,這時候即便是volatile也不能保證lower < upper這個不變式始終成立。併發

場景三:變量須要加鎖訪問的狀況,由於已經用了鎖,volatile顯然是多餘的。jvm

CAS

特色:性能

  1. 一種輕量級的鎖,基於硬件CPU指令實現。這是一種樂觀鎖,假設不會產生衝突而不加鎖地進行操做,若是操做失敗就不停地重試,直到操做成功。是一種非阻塞的機制。優化

  2. 原理:提供3個操做數,分別是內存位置V,原有的預期值A,要修改的新值B,只有當A=V時,才把V的值修改成B,不然,一直重試,直到成功。.net

    public final int incrementAndGet() {
     	for (;;) {
         	int current = get();
         	int next = current + 1;
         	if (compareAndSet(current, next))
             	return next;
     	}
     }

問題:

  1. ABA的問題:大部分場景下(關注值的最終結果,不關注過程),咱們不須要關心這個問題。
  2. 循環開銷問題:若是CAS操做一直失敗,會長時間佔用cpu資源。

synchronized

特色:

  1. jvm的內置的鎖機制,重量級的鎖,獨佔鎖,悲觀鎖,可重入鎖。
  2. 做用於代碼塊,自動釋放鎖。
  3. 使用簡單,jvm自己作了不少優化。(優先仍是要使用這個鎖)

ReentrantLock

特色:

  1. 提供與synchronized相同的互斥性和內存可見性。重量級鎖,獨佔鎖,悲觀鎖。
  2. 比synchronized有更靈活的加鎖機制。可定時,可輪詢,可中斷,公平性選擇。 ps:在一些併發訪問的業務場景下,能夠用ReentrantLock來靈活地控制併發操做,其可中斷的特色能夠很好地控制多線程併發的擁堵問題。
  3. 必須在finally中手動釋放鎖。

ReentrantReadWriteLock

特色:

  1. 無論是synchronized仍是ReentrantLock,都是強類型的互斥鎖,併發狀況下的性能會受影響。可是在一些應用場景下,可能讀操做要多於寫操做,在沒有寫操做的時候,若是用這種強互斥鎖的話,會嚴重影響讀線程的性能。因此能夠考慮用ReentrantReadWriteLock來代替上面的強互斥鎖,讓多個讀線程能夠併發地訪問被保護的對象,提升吞吐量。PS:能夠在LinkedHashMap上使用ReentrantReadWriteLock來實現高性能的LRU Cache.

  2. 另一種讀寫鎖的實現是經過volatile(保證讀可見) + synchronized(保證寫原子性)來實現。

鎖優化和使用

  1. 儘可能縮小鎖定的範圍(好比synchronized,能指定代碼塊就不要在方法上加鎖,儘可能只包含須要受保護的對象的操做)。
  2. 鎖分離技術,把須要受保護的對象拆分紅多個小對象,每一個小對象都由獨立的鎖來保護。(能夠參考ConcurrentHashMap)
  3. 從性能上來講:volatile > cas > synchronized,因此在合適的場景下能夠選擇性能更好的方案。

參考資料:

  1. 《java併發編程實戰》
  2. http://www.ibm.com/developerworks/cn/java/j-jtp06197.html

推薦閱讀:

Java基礎——經常使用Map的實現細節

MySQL使用與優化總結

相關文章
相關標籤/搜索