天天看點

CAS 無鎖優化=自旋=樂觀鎖

atomicXXX類

  • Compare and Set/Swap 比較并且設定
  • cas(V,Expected,NewValue)

    if V == E

    V == New

    otherwise try again or fail

  • CPU原語支援

    atomic底層調用到UnSafe這個方法,三個參數V就是目前版本值,Expected期望值,NewValue賦予的新值,隻有當V等于E的時候才将新的值賦給這個變量,又因為它是原語支援是CPU級别的,是一個原子操作是以在設值時不會有其他線程來插隊設值。

實作都是CAS

/**
 * 解決同樣的問題的更高效的方法,使用AtomXXX類
 * AtomXXX類本身方法都是原子性的,但不能保證多個方法連續調用是原子性的
 * @author 
 */
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;


public class T01_AtomicInteger {
	/*volatile*/ //int count1 = 0;

	AtomicInteger count = new AtomicInteger(0); 

	/*synchronized*/ void m() { 
		for (int i = 0; i < 10000; i++)
			//if count1.get() < 1000
			count.incrementAndGet(); //count1++
	}

	public static void main(String[] args) {
		T01_AtomicInteger t = new T01_AtomicInteger();

		List<Thread> threads = new ArrayList<Thread>();

		for (int i = 0; i < 10; i++) {
			threads.add(new Thread(t::m, "thread-" + i));
		}

		threads.forEach((o) -> o.start());

		threads.forEach((o) -> {
			try {
				o.join();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		});

		System.out.println(t.count);

	}

}           

複制

ABA問題

如果果另一個線程修改V值假設原來是A,先修改成B,再修改回成A。目前線程的CAS操作無法分辨目前V值是否發生過變化。

解決:

  • 在CAS的時候加版本号,每次操作比較下版本号
  • 加 version
  • A 1.0
  • B 2.0
  • A 3.0
  • cas(version)
  • 原子類 AtomicStampedReference解決ABA問題
  • ABA問題重要不?如果是基本資料類型結果沒影響,如果是引用對象就不好說了,比如你的女朋友和你複合,前面經過了多少個其他XXX,你覺得有影響不?

    unsafe

    • 直接操作記憶體

      allocateMemory putXX freeMemory pageSize

    • 直接生成類執行個體

      allocateInstance

    • 直接操作類或執行個體變量

      objectFieldOffset

      getInt

      getObjecct

    • CAS相關操作

      compareAndSwapObject Int long(JDK1.8)