volatile 關鍵字 (二)
文章來自Java Guide 用于學習如有侵權,立即刪除
volatile 可以保證原子性么?
volatile
關鍵字能保證變量的可見性,但不能保證對變量的操作是原子性的。
我們通過下面的代碼即可證明:
/*** 微信搜 JavaGuide 回復"面試突擊"即可免費領取個人原創的 Java 面試手冊** @author Guide哥* @date 2022/08/03 13:40**/
public class VolatoleAtomicityDemo {public volatile static int inc = 0;public void increase() {inc++;}public static void main(String[] args) throws InterruptedException {ExecutorService threadPool = Executors.newFixedThreadPool(5);VolatoleAtomicityDemo volatoleAtomicityDemo = new VolatoleAtomicityDemo();for (int i = 0; i < 5; i++) {threadPool.execute(() -> {for (int j = 0; j < 500; j++) {volatoleAtomicityDemo.increase();}});}// 等待1.5秒,保證上面程序執行完成Thread.sleep(1500);System.out.println(inc);threadPool.shutdown();}
}
正常情況下,運行上面的代碼理應輸出 2500
。但你真正運行了上面的代碼之后,你會發現每次輸出結果都小于 2500
。
為什么會出現這種情況呢?不是說好了,volatile
可以保證變量的可見性嘛!
也就是說,如果 volatile
能保證 inc++
操作的原子性的話。每個線程中對 inc
變量自增完之后,其他線程可以立即看到修改后的值。5 個線程分別進行了 500 次操作,那么最終 inc 的值應該是 5*500=2500。
很多人會誤認為自增操作 inc++
是原子性的,實際上,inc++
其實是一個復合操作,包括三步:
- 讀取 inc 的值。
- 對 inc 加 1。
- 將 inc 的值寫回內存。
volatile
是無法保證這三個操作是具有原子性的,有可能導致下面這種情況出現:
- 線程 1 對
inc
進行讀取操作之后,還未對其進行修改。線程 2 又讀取了inc
的值并對其進行修改(+1),再將inc
的值寫回內存。 - 線程 2 操作完畢后,線程 1 對
inc
的值進行修改(+1),再將inc
的值寫回內存。
這也就導致兩個線程分別對 inc
進行了一次自增操作后,inc
實際上只增加了 1。
其實,如果想要保證上面的代碼運行正確也非常簡單,利用 synchronized
、Lock
或者AtomicInteger
都可以。
使用 synchronized
改進:
public synchronized void increase() {inc++;
}
使用 AtomicInteger
改進:
public AtomicInteger inc = new AtomicInteger();public void increase() {inc.getAndIncrement();
}
使用 ReentrantLock
改進:
Lock lock = new ReentrantLock();
public void increase() {lock.lock();try {inc++;} finally {lock.unlock();}
}
大家好,我是xwhking,一名技術愛好者,目前正在全力學習 Java,前端也會一點,如果你有任何疑問請你評論,或者可以加我QQ(2837468248)說明來意!希望能夠與你共同進步