validate 關鍵字可以保證多線程之間的可見性,但是不能保證原子操作。(需要了解java內存模型jmm)
package com.cn.test.thread; public class VolatileAutomic extends Thread{ private volatile static int count = 0; public static void add() { for (int i = 0; i < 10000; i++) { count ++; } } @Override public void run() { add(); } public static void main(String[] args) { for (int i = 0; i < 10; i++) { new VolatileAutomic().start(); } while (Thread.activeCount() > 1) { Thread.yield(); } // System.out.println(atomicCount.get()); System.out.println("count=" + count); } }
運行結果:
count=61618
上面例子中volatile關鍵字能保證可見性沒有錯,但是上面的程序錯在沒能保證原子性。可見性只能保證每次讀取的是最新的值,但是volatile沒辦法保證對變量的操作的原子性。
count++是一個非原子性的操作,它包括讀取變量的原始值、進行加1操作、寫入工作內存。那么就是說自增操作的三個子操作可能會分割開執行,就有可能導致的情況:
假如某個時刻變量count的值為10,線程1對變量進行自增操作,線程1先讀取了變量count的原始值,然后線程1被阻塞了;線程2也對變量進行自增操作,線程2先讀取了count的原始值,線程1只是進行了讀取操作,沒有進行寫的操作,所以不會導致線程2中的本地緩存無效,因此線程2進行++操作,在把結果刷新到主存中去,此時線程1在還是讀取原來的10 的值在進行++操作,所以線程1和線程2對於count=10進行兩次++操作,結果都為11.。
上述問題解決方法:
1.采用add方法中加入sychnorized.,或者同步代碼塊
2.采用AtomicInteger
package com.cn.test.thread; import java.util.concurrent.atomic.AtomicInteger; public class VolatileAutomic extends Thread{ static AtomicInteger atomicCount = new AtomicInteger(0); public static void add() { for (int i = 0; i < 10000; i++) { atomicCount.incrementAndGet(); } } @Override public void run() { add(); } public static void main(String[] args) { for (int i = 0; i < 10; i++) { new VolatileAutomic().start(); } while (Thread.activeCount() > 1) { Thread.yield(); } System.out.println(atomicCount.get()); } }
運行結果:
100000
AtomicInteger是java.util.concurrent.atomic並發包下面一個類。
AtomicInteger源碼理解:
static { try { valueOffset = unsafe.objectFieldOffset (AtomicInteger.class.getDeclaredField("value")); } catch (Exception ex) { throw new Error(ex); } }
/** * Creates a new AtomicInteger with the given initial value. * * @param initialValue the initial value */ public AtomicInteger(int initialValue) { value = initialValue; }
調用AtomicInteger的構造方法傳入一個initialValue值,將initialValue復制給一個volatile 的value,
/**
* Atomically increments by one the current value.
*
* @return the updated value
*/
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while (!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
底層源碼實現主要采用cas算法的思想,通過操作底層硬件的指令。cas鎖是一種無鎖的機制。cas的機制:v的值應該是A,如果是A則將值更新為B。當多個線程同時要更新V的值,
只有其中一個線程更新成功,其他線程更新失敗,更新失敗的線程並不會掛起,而是在繼續進行競爭資源,對V的值進行操作。
cas鎖的機制采用的是事物提交-- 失敗--重試來保證原子性。
cas鎖的缺點:當並發量大的時候,多線程一直進行重試,這樣會導致CPU的開銷很大。
package com.cn.test.thread; import java.util.concurrent.atomic.AtomicInteger; /** * 設計4個線程,其中兩個線程 每次對j增加1,另外兩個線程對j每次減少1。寫出程序。 * */ public class TestAtomicThread extends Thread { private static int j = 10; static AtomicInteger atomicInteger = new AtomicInteger(j); public TestAtomicThread(String string) { super(string); } @Override public void run() { System.out.println(Thread.currentThread().getName()); switch(Thread.currentThread().getName()) { case "thread-1": case "threa-2": atomicInteger.incrementAndGet(); // System.out.println("當前線程名稱:" + Thread.currentThread().getName() + ":" + atomicInteger.get()); break; case "thread-3": case "thread-4": atomicInteger.decrementAndGet(); // System.out.println("當前線程名稱:" + Thread.currentThread().getName() + ":" + atomicInteger.get()); break; default : break; } } public static void main(String[] args) { TestAtomicThread t1 = new TestAtomicThread("thread-1"); TestAtomicThread t2 = new TestAtomicThread("thread-2"); TestAtomicThread t3 = new TestAtomicThread("thread-3"); TestAtomicThread t4 = new TestAtomicThread("thread-4"); t1.start(); t2.start(); t3.start(); t4.start(); if (Thread.activeCount() > 1) { Thread.yield(); } System.out.println("j====" + atomicInteger.get()); } }
