日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【转载】JAVA内存模型和线程安全

發(fā)布時(shí)間:2024/4/13 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【转载】JAVA内存模型和线程安全 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

本文轉(zhuǎn)載自http://shift-alt-ctrl.iteye.com/blog/1845309

?

一.JAVA內(nèi)存模型(JMM,JAVA Memory Model):

??? 運(yùn)行時(shí)涉及到兩種內(nèi)存,主內(nèi)存和工作區(qū)內(nèi)存,其中工作區(qū)內(nèi)存通常為CPU的高速緩存區(qū)用來加快內(nèi)存數(shù)據(jù)讀取操作的(各線程獨(dú)立).所有的變量內(nèi)容都存在主內(nèi)存中,當(dāng)需要對(duì)內(nèi)存數(shù)據(jù)進(jìn)行操作時(shí),數(shù)據(jù)將會(huì)從主存中l(wèi)oad到工作區(qū)緩存并由CPU計(jì)算和賦值操作,然后再由工作區(qū)內(nèi)存write到主存中,讀取時(shí)如果工作區(qū)內(nèi)存中已經(jīng)有(loaded)則直接使用;工作區(qū)內(nèi)存保存了線程使用的變量的副本,線程不可以直接操作主內(nèi)存,只能操作工作區(qū)內(nèi)存,對(duì)于需要變更的變量,需要通過一系列回寫指令集同步到主內(nèi)存中.且工作區(qū)內(nèi)存是線程獨(dú)占的,主內(nèi)存是線程共享的.如下為操作集:

  • lock:對(duì)主內(nèi)存中的變量"加鎖",標(biāo)記為一個(gè)線程持有.如果一個(gè)變量已經(jīng)被lock,其他線程嘗試lock將被阻塞,同一個(gè)線程可以多次lock,不過鎖的引用次數(shù)將會(huì)+1,需要unlock同樣次數(shù),才能解鎖.對(duì)一個(gè)變量lock將會(huì)導(dǎo)致清空工作區(qū)內(nèi)存中此變量的副本,即當(dāng)其他線程再次使用此變量時(shí)需要重新獲取.
  • unlock:對(duì)主內(nèi)存中的變量"釋放鎖",釋放鎖的線程和持有鎖的線程必須是同一個(gè)線程,無法對(duì)沒有加鎖的變量執(zhí)行unlock.
  • read:由工作區(qū)內(nèi)存向主內(nèi)存發(fā)出"read"操作,隨后必須執(zhí)行l(wèi)oad操作.
  • load:工作區(qū)內(nèi)存中加載"read"操作指定的變量,并放入副本中.此指令需要和read保持順序
  • use:變量交付給執(zhí)行引擎做計(jì)算,當(dāng)JVM需要使用變量時(shí),將會(huì)使用此操作.
  • assign:在工作區(qū)內(nèi)存中,將變量更新為某個(gè)值.一個(gè)被assign操作的變量,必須被write到主內(nèi)存,如果沒有被assign的變量不能被write到主內(nèi)存.
  • store:工作區(qū)內(nèi)存向主內(nèi)存發(fā)出"同步"操作.
  • write:工作區(qū)內(nèi)存將store操作指定的變量同步到主內(nèi)存中.此操作需要和store保持順序.
  • 其中read->load,store->write指令必須按照順序執(zhí)行,即不能load一個(gè)沒有被read操作指定的變量,也不能write一個(gè)沒有被store操作指定的變量,不過這read + load/store + write不一定必須是連續(xù)的,其中間仍然可以有其他指令.(volatile有特例)

    ??? volatile是java提供的輕量級(jí)變量同步機(jī)制,它確保了變量可見性,即任何一個(gè)線程修改了volatile修飾的變量,其他線程將立即可見.對(duì)于普通變量因?yàn)榇嬖谥鲀?nèi)存和工作區(qū)內(nèi)存的復(fù)制和同步,所以無法具備此特性.volatile變量存儲(chǔ)在主內(nèi)中,任何線程需要使用此變量時(shí),必須再次read,因?yàn)槠渌€程對(duì)此變量的更改,將會(huì)被其他線程在使用此變量時(shí)立即獲得新值.

    ??? volatile只是保證了可見性,但是它并非線程安全,因?yàn)槿绻€程一旦read到此值然后進(jìn)行計(jì)算,在尚未write到主內(nèi)存時(shí)其他線程也做了同樣的操作,那么volatile變量的最終結(jié)果將無法達(dá)到預(yù)期..如果期望volatile變量線程安全,必須同步或者CAS.volatile變量操作時(shí),read->load->use三個(gè)操作是連續(xù)的,assign->store->write三個(gè)操作是連續(xù)的.

    ? ? 通常volatile對(duì)“值”類型的對(duì)象是有效的,對(duì)引用類型是沒有意義的。

    ?

    二.線程安全

    ????線程是執(zhí)行任務(wù)的最小調(diào)度單元,內(nèi)核線程是OS創(chuàng)建和管理的線程,它將有內(nèi)核完成線程的切換以及調(diào)度(CPU調(diào)度).任何一個(gè)java線程都對(duì)應(yīng)一個(gè)內(nèi)核線程,即java線程的所有特性都基于內(nèi)核并受制于內(nèi)核.在linux和windows系統(tǒng)中,一個(gè)java線程就是底層的一個(gè)內(nèi)核線程.java對(duì)線程的調(diào)度基于內(nèi)核,在主流的系統(tǒng)中,廣泛采用了"搶占式"調(diào)度機(jī)制,即線程都以"爭搶CPU資源"的姿態(tài)來運(yùn)行,最終被運(yùn)行的線程將有內(nèi)核的調(diào)度算法來決定,如果線程沒有獲得運(yùn)行資源,那么線程將被"暫停".."協(xié)同式"調(diào)度已經(jīng)不適合多線程(進(jìn)程)的系統(tǒng),它表現(xiàn)為線程之間互相"謙讓",如果一個(gè)線程獲得運(yùn)行資源,那么它將一直運(yùn)行下去直到結(jié)束,如果一個(gè)線程是"長時(shí)間"的,那么極有可能這個(gè)線程將獨(dú)占一個(gè)CPU,而其他線程無法獲得資源..

    ?? 線程狀態(tài):

  • NEW:新創(chuàng)建線程,尚未開啟.
  • RUNNABLE:當(dāng)前線程已經(jīng)被啟動(dòng)或者正在被運(yùn)行,處于此狀態(tài)的線程標(biāo)明即將或者已經(jīng)得到了運(yùn)行資源.
  • WAITING:如果線程因?yàn)閣ait()/join()/LockSupport.park(this)/sleep()導(dǎo)致當(dāng)前線程無法繼續(xù)執(zhí)行或者獲得資源.
  • BLOCKED:如果當(dāng)前線程因?yàn)閷?duì)象鎖獲取時(shí),被"阻塞",那么線程將處于BLOCKED狀態(tài),此狀態(tài)下,線程不會(huì)釋放資源.
  • TERMINATED:線程執(zhí)行結(jié)束,資源即將被回收.
  • ??? 在JAVA中(甚至任何語言或者平臺(tái)中)確保線程安全的方式,無外乎"同步鎖"和"CAS","同步鎖"是一種粗暴而嚴(yán)格的同步手段,它強(qiáng)制對(duì)資源的訪問必須隊(duì)列化,一個(gè)資源在任何時(shí)候只能有一個(gè)線程可訪問.在java中"synchronized"修飾詞可以用來同步方法的調(diào)用,synchronized可以指定需要同步的對(duì)象,如果 沒有指定,默認(rèn)為當(dāng)前對(duì)象,如果是static方法,則表示對(duì)Class同步.synchronized關(guān)鍵詞在編譯之后,最終會(huì)生成2個(gè)指令:monitorenter和monitorexit,執(zhí)行引擎如果遇到monitorenter指令,將會(huì)嘗試獲取對(duì)象鎖,如果獲取成功,則鎖計(jì)數(shù)器+1,同時(shí)工作區(qū)中的對(duì)象值將視為無效,重新從主存中l(wèi)oad;monitorexit將導(dǎo)致鎖計(jì)數(shù)器-1,即釋放鎖,此時(shí)將會(huì)把對(duì)象值從工作區(qū)緩存中write到主存中;如果計(jì)數(shù)器為0,則表示此對(duì)象沒有被任何線程加鎖.如果獲取鎖失敗,當(dāng)前線程阻塞.此外synchronized本身具有"重入性"語義,如果此對(duì)象上的monitor是當(dāng)前線程,那么鎖獲取操作將直接成功.

    ?? 我們不再爭論synchronized鎖和ReentrantLock API鎖誰更優(yōu)秀,這一把雙刃劍,性能方面兩者在普通情況下(即無復(fù)雜遞深的lock調(diào)用或者多層synchronized)性能幾乎差不多,synchronized稍微優(yōu)秀一些.但是ReentrantLock提供了多樣化的控制以及Condition機(jī)制,可以幫助我們有效的控制并發(fā)環(huán)境中,讓線程遵循條件的阻塞和喚醒;例如BlockingQueue的實(shí)現(xiàn)機(jī)制.

    ??? CAS(Compare and swap),設(shè)計(jì)方式上更像一種"樂觀鎖",通過"比較"-"更新"這種無阻塞的手段實(shí)現(xiàn)數(shù)據(jù)在多線程下的"安全性".在JAVA中CAS操作遍布Atomic包下的API中,底層使用一個(gè)閉源的Unsafe.compareAndSwapInt(Object,valueOffset,expect,update),其中需要告知對(duì)象的內(nèi)存地址.CAS會(huì)出現(xiàn)一個(gè)有趣的問題,就是ABA,即A變量被更改為B之后,再次被更改為A,此時(shí)對(duì)于持有A數(shù)據(jù)的線程嘗試更改值是可以成功了,就像B值從來就沒有出現(xiàn)過一樣..其實(shí)吧,這個(gè)問題不是問題,既然有線程把數(shù)據(jù)更改為A,那么后續(xù)的線程操作就應(yīng)該遵守現(xiàn)在的結(jié)果,而無需關(guān)注過去的過程.

    總結(jié)

    以上是生活随笔為你收集整理的【转载】JAVA内存模型和线程安全的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。