Java SE 多線程安全問(wèn)題產(chǎn)生的原因?
問(wèn)題描述
可能像圖片上的代碼出現(xiàn)負(fù)數(shù)的概率不大,但在if語(yǔ)句后加上Thread.sleep(10);就能看到輸出負(fù)數(shù)
問(wèn)題解答
回答1:不知道你要問(wèn)什么,多個(gè)線程同時(shí)讀取一個(gè)資源出現(xiàn)不同步問(wèn)題很正常,因?yàn)榭赡芤粋€(gè)線程獲取值的時(shí)候另一個(gè)線程恰好在寫值,這就會(huì)產(chǎn)生同步問(wèn)題。
解決辦法有很多,最笨的直接代碼塊上加同步,整個(gè)鎖起來(lái);好點(diǎn)的是用線程安全的類,比如AtomInteger這種,保證同步;如果對(duì)多線程很有研究,甚至可以只加很少的鎖就能完成任務(wù)。
回答2:線程的調(diào)用順序是不保證有序的,其根本原因在于JVM協(xié)調(diào)資源時(shí)線程之間的切換。
回答3:本質(zhì)原因是CPU為了提高效率會(huì)對(duì)指令進(jìn)行重排序
回答4:沒(méi)有對(duì)num進(jìn)行同步,不能保證當(dāng)前線程對(duì)num的值改之后,其他線程可以立馬看到,題主可以了解下Java內(nèi)存模型。 以題主的代碼為例,假設(shè)執(zhí)行到最后num=1,三個(gè)線程同時(shí)執(zhí)行到if判斷,都能判斷出通過(guò),那就有可能出現(xiàn)負(fù)數(shù)。
回答5:1、內(nèi)存可見性2、修改的原子性
由于num是類靜態(tài)變量,那么它會(huì)被存到堆中,在run()方法執(zhí)行時(shí)拷貝一份副本到棧中存儲(chǔ),當(dāng)有多個(gè)線程修改時(shí),可能同時(shí)拿到一樣的副本,但是由于執(zhí)行的前后順序,一個(gè)線程修改并寫入了該變量,雖然堆中num已經(jīng)發(fā)生變化,但是其他線程并不知道,它們會(huì)繼續(xù)修改那份副本。然后修改后寫入堆中,那這樣就會(huì)覆蓋之前線程的修改,進(jìn)而導(dǎo)致狀態(tài)的不一致問(wèn)題。那么如果才能確保線程安全性呢。那就要確保修改num之前保證對(duì)堆區(qū)修改的可見性,修改之前再拿一份副本(即使之前已經(jīng)拿過(guò)了),這個(gè)可用volatile關(guān)鍵字來(lái)保證。
原子性,由于num--實(shí)際執(zhí)行是兩個(gè)操作,那么就會(huì)存在執(zhí)行順序問(wèn)題。即使在前面說(shuō)過(guò)用volatilel來(lái)保證可見性。但是還會(huì)存在修改被其他線程覆蓋的情形,只不過(guò)幾率變小了。怎樣保證原子性呢,可以采用synchronized關(guān)鍵字,Lock機(jī)制,以及JDK并發(fā)工具包等。對(duì)于這種情形,最簡(jiǎn)單的辦法就是
private static AtomicInteger num=new AtomicInteger(100);
相關(guān)文章:
1. javascript - 原生canvas中如何獲取到觸摸事件的canvas內(nèi)坐標(biāo)?2. showpassword里的this 是什么意思?代表哪個(gè)元素3. mysql scripts提示 /usr/bin/perl: bad interpreter4. css3 - css怎么實(shí)現(xiàn)圖片環(huán)繞的效果5. html - vue項(xiàng)目中用到了elementUI問(wèn)題6. 對(duì)mysql某個(gè)字段監(jiān)控的功能7. android - 用textview顯示html時(shí)如何寫imagegetter獲取網(wǎng)絡(luò)圖片8. JavaScript事件9. css3 - border-bottom 的長(zhǎng)度可否超過(guò)盒子的寬度呢?實(shí)現(xiàn)如下圖效果。(我的書下面的線)10. mysql優(yōu)化 - mysql EXPLAIN之后怎么看結(jié)果進(jìn)行優(yōu)化 ?
