一道非常棘手的 Java 面试题:i++ 是线程安全的吗
轉(zhuǎn)載自??一道非常棘手的 Java 面試題:i++ 是線程安全的嗎
i++ 是線程安全的嗎?
相信很多中高級(jí)的 Java 面試者都遇到過(guò)這個(gè)問(wèn)題,很多對(duì)這個(gè)不是很清楚的肯定是一臉蒙逼。內(nèi)心肯定還在質(zhì)疑,i++ 居然還有線程安全問(wèn)題?只能說(shuō)自己了解的不夠多,自己的水平有限。
先來(lái)看下面的示例來(lái)驗(yàn)證下 i++ 到底是不是線程安全的。
1000個(gè)線程,每個(gè)線程對(duì)共享變量 count 進(jìn)行 1000 次 ++ 操作。
static int count = 0; static CountDownLatch cdl = new CountDownLatch(1000);/** * 微信公眾號(hào):Java面經(jīng) */public static void main(String[] args) throws Exception {CountRunnable countRunnable = new CountRunnable();for (int i = 0; i < 1000; i++) {new Thread(countRunnable).start();}cdl.await();System.out.println(count); }static class CountRunnable implements Runnable {private void count() {for (int i = 0; i < 1000; i++) {count++;}}@Overridepublic void run() {count();cdl.countDown();}}上面的例子我們期望的結(jié)果應(yīng)該是 1000000,但運(yùn)行 N 遍,你會(huì)發(fā)現(xiàn)總是不為 1000000,至少你現(xiàn)在知道了 i++ 操作它不是線程安全的了。
先來(lái)看 JMM 模型中對(duì)共享變量的讀寫(xiě)原理吧。
?
每個(gè)線程都有自己的工作內(nèi)存,每個(gè)線程需要對(duì)共享變量操作時(shí)必須先把共享變量從主內(nèi)存 load 到自己的工作內(nèi)存,等完成對(duì)共享變量的操作時(shí)再 save 到主內(nèi)存。
問(wèn)題就出在這了,如果一個(gè)線程運(yùn)算完后還沒(méi)刷到主內(nèi)存,此時(shí)這個(gè)共享變量的值被另外一個(gè)線程從主內(nèi)存讀取到了,這個(gè)時(shí)候讀取的數(shù)據(jù)就是臟數(shù)據(jù)了,它會(huì)覆蓋其他線程計(jì)算完的值。。。
這也是經(jīng)典的內(nèi)存不可見(jiàn)問(wèn)題,那么把 count 加上 volatile 讓內(nèi)存可見(jiàn)是否能解決這個(gè)問(wèn)題呢??答案是:不能。因?yàn)?volatile 只能保證可見(jiàn)性,不能保證原子性。多個(gè)線程同時(shí)讀取這個(gè)共享變量的值,就算保證其他線程修改的可見(jiàn)性,也不能保證線程之間讀取到同樣的值然后相互覆蓋對(duì)方的值的情況。
關(guān)于多線程的幾種關(guān)鍵概念請(qǐng)翻閱《多線程之原子性、可見(jiàn)性、有序性詳解》這篇文章。
解決方案
說(shuō)了這么多,對(duì)于 i++ 這種線程不安全問(wèn)題有沒(méi)有其他解決方案呢?當(dāng)然有,請(qǐng)參考以下幾種解決方案。
1、對(duì) i++ 操作的方法加同步鎖,同時(shí)只能有一個(gè)線程執(zhí)行 i++ 操作;
2、使用支持原子性操作的類,如?java.util.concurrent.atomic.AtomicInteger,它使用的是 CAS 算法,效率優(yōu)于第 1 種;
總結(jié)
以上是生活随笔為你收集整理的一道非常棘手的 Java 面试题:i++ 是线程安全的吗的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 仅理想前三季度完成年度销量目标 小鹏、蔚
- 下一篇: Java 程序员必须掌握的 5 个注解