標籤:
各種不同的情況保證資料的正確性,完整性。
public class TestMultiThread implements Runnable { private static int i; private static volatile Integer vi = 0; private static AtomicInteger ai = new AtomicInteger(); private static Integer si = 0; private static Integer ia = 1; private static int ri; private static AtomicInteger flag = new AtomicInteger(); private static Lock lock = new ReentrantLock(); //如果lock不加static關鍵字時,那麼每一個線程將鎖住的是具體的對象, //而不是這個類。就會照成資料的不正確性 @Override public void run() { for (int k = 0; k < 200000; k++) { i++; //不是原子操作,此處含有三步(取得i的值,i+1,把i+1後的結果賦值給i)結果將有異常。當一條線程執行到i+1時,還沒有賦值,另一線程i+1並賦值。 vi++; //同上,②解釋 ai.incrementAndGet(); //可以用原子方式更新的 int 值。以原子方式將當前值加 1.它只有一步操作,隨便哪個線程執行都將只有這一步。 // synchronized(si){ // 此方法會照成si的值不對,是應為同步語句鎖定的是對象,用Integer作為對象鎖來使用,這本身沒有任何問題。 // si++; // 但同步體裡的操作偏偏是更改當前的鎖對象,結果就是前的對象鎖其實已經改變了,不是針對唯一的一個對象加鎖,所以會出現問題。 // 也因此,換一個公用對象就能解決問題。 // 在++操作中其實有一個賦值操作,這時對象已經改變,或者說是非原子操作 // } synchronized (ia) { //用不變的ia作為鎖對象就能保證資料的正確性 si++; } lock.lock(); //此處的lock鎖必須為static的 try { ri++; } finally { lock.unlock(); } } flag.incrementAndGet(); } public static void main(String[] args) throws InterruptedException { TestMultiThread t1 = new TestMultiThread(); TestMultiThread t2 = new TestMultiThread(); ExecutorService exec1 = Executors.newCachedThreadPool(); ExecutorService exec2 = Executors.newCachedThreadPool(); exec1.execute(t1); exec2.execute(t2); while (true) { if (flag.intValue() == 2) { System.out.println("i>>>>>" + i); System.out.println("vi>>>>>" + vi); System.out.println("ai>>>>>" + ai); System.out.println("si>>>>>" + si); System.out.println("ri>>>>>" + ri); break; } Thread.sleep(50); } }}
結果:
i>>>>>398950
vi>>>>>386295
ai>>>>>400000
si>>>>>400000
ri>>>>>400000
解釋②:
在 java 記憶體回收整理一文中,描述了jvm運行時刻記憶體的分配。其中有一個記憶體地區是jvm虛擬機器棧,每一個線程運行時都有一個線程棧,
線程棧儲存了線程運行時候變數值資訊。當線程訪問某一個對象時候值的時候,首先通過對象的引用找到對應在堆記憶體的變數的值,然後把堆記憶體
變數的具體值load到執行緒區域記憶體中,建立一個變數副本,之後線程就不再和對象在堆記憶體變數值有任何關係,而是直接修改副本變數的值,
在修改完之後的某一個時刻(線程退出之前),自動把線程變數副本的值回寫到對象在堆中變數。這樣在堆中的對象的值就產生變化了。
java多線程中關於原子操作