線程學(xué)習(xí)_03線程相關(guān)概念

物理內(nèi)存 :

??由于計(jì)算機(jī)的存儲(chǔ)設(shè)備與處理器的運(yùn)算速度有幾個(gè)數(shù)量級(jí)的差距, 所以現(xiàn)代計(jì)算機(jī)系統(tǒng)都不得不加入一層讀寫速度盡可能接近處理器運(yùn)算速度的高速緩存來作為內(nèi)存與處理器之間的緩存: 將運(yùn)算需要使用到的數(shù)據(jù)復(fù)制到緩存中, 讓運(yùn)算能快速進(jìn)行, 當(dāng)運(yùn)算結(jié)束后再從緩存同步回內(nèi)存中, 這樣處理器就無須等待緩慢的內(nèi)存讀寫了;

緩存一致性 :

??在多處理器系統(tǒng)中, 每個(gè)處理器都有自己的高速緩存, 而它們又共享同一主內(nèi)存. 當(dāng)多個(gè)處理器的運(yùn)算任務(wù)都涉及同一塊主內(nèi)存區(qū)域時(shí), 將可能導(dǎo)致各自的緩存數(shù)據(jù)不一致.


image.png
指令重排序 :

??為了使得處理器內(nèi)部的運(yùn)算單元能盡量被充分利用, 處理器可能會(huì)對(duì)輸入代碼進(jìn)行亂序執(zhí)行優(yōu)化, 處理器會(huì)在計(jì)算之后將亂序執(zhí)行的結(jié)果重組, 保證該結(jié)果與順序執(zhí)行的結(jié)果是一致的, 但并不保證程序中各個(gè)語句計(jì)算的先后順序與輸入代碼的順序一致, 因此, 如果存在一個(gè)計(jì)算任務(wù)依賴另一個(gè)計(jì)算任務(wù)的中間結(jié)果, 那么其順序性并不能靠代碼的先后順序來保證;

java內(nèi)存模型 :

??1、java內(nèi)存模型的主要目標(biāo)是定義程序中各個(gè)變量的訪問規(guī)則, 此處的變量包括了實(shí)例字段, 靜態(tài)字段和構(gòu)成數(shù)組對(duì)象的元素, 但不包括局部變量與方法參數(shù), 因?yàn)楹笳呤蔷€程私有的, 不會(huì)被共享, 不會(huì)存在競(jìng)爭(zhēng)的問題;
??2、java內(nèi)存模型規(guī)定了所有的變量都存儲(chǔ)在主內(nèi)存中. 每條線程還有自己的工作內(nèi)存, 線程的工作內(nèi)存中保存了該線程使用到的變量的主內(nèi)存副本拷貝, 線程對(duì)變量的所有操作(讀取, 賦值等)都必須在工作內(nèi)存中進(jìn)行, 而不能直接讀寫主內(nèi)存中的變量.
??3、不同的線程之間也無法直接訪問對(duì)方工作內(nèi)存中的變量, 線程間變量值得傳遞均需要通過主內(nèi)存來完成.
??4、工作內(nèi)存對(duì)應(yīng)虛擬機(jī)棧中的部分區(qū)域, 主內(nèi)存對(duì)應(yīng)于物理硬件的內(nèi)存;


image.png
內(nèi)存間交互 :

??主內(nèi)存與工作內(nèi)存之間具體的交互協(xié)議 : java內(nèi)存模型中定義了以下8種操作來完成, 一個(gè)變量在主內(nèi)存和工作內(nèi)存之間的交互的實(shí)現(xiàn)細(xì)節(jié), 虛擬機(jī)實(shí)現(xiàn)時(shí)必須保證下面提及的每一種操作都是原子的、不可再分的;

  • 1、lock(鎖定) : 作用于主內(nèi)存的變量, 它把一個(gè)變量標(biāo)識(shí)為一條線程獨(dú)占的狀態(tài);
  • 2、unlock(解鎖) : 作用于主內(nèi)存的變量, 它把一個(gè)處于鎖定狀態(tài)的變量釋放出來, 釋放后的變量才可以被其他線程鎖定;
  • 3、read(讀取) : 作用于主內(nèi)存的變量, 它把一個(gè)變量的值從主內(nèi)存?zhèn)鬏數(shù)骄€程的工作內(nèi)存中, 以便隨后的load動(dòng)作使用;
  • 4、load(載入) : 作用于工作內(nèi)存的變量, 它把read操作從主內(nèi)存中得到的變量值放入工作內(nèi)存的變量副本中;
  • 5、use(使用) : 作用于工作內(nèi)存的變量, 它把工作內(nèi)存中一個(gè)變量的值傳遞給執(zhí)行引擎, 每當(dāng)虛擬機(jī)遇到一個(gè)需要使用的變量的值得字節(jié)碼指令時(shí)將會(huì)執(zhí)行這個(gè)操作;
  • 6、assign(賦值) : 作用于工作內(nèi)存的變量, 它把一個(gè)從執(zhí)行引擎接收到的值賦給工作內(nèi)存的變量, 每當(dāng)虛擬機(jī)遇到一個(gè)給變量賦值的字節(jié)碼指令時(shí)執(zhí)行這個(gè)操作;
  • 7、store(存儲(chǔ)) : 作用于工作內(nèi)存的變量, 它把工作內(nèi)存中一個(gè)變量的值傳送到主內(nèi)存中, 以便隨后的write操作使用;
  • 8、write(寫入) : 作用于主內(nèi)存的變量, 它把store操作從工作內(nèi)存中得到的變量的值放入主內(nèi)存的變量中;
volatile :

??1、可見性 : 保證變量對(duì)所有線程可見, 當(dāng)一條線程修改了這個(gè)變量的值, 新值對(duì)于其他線程來說是可以立即得知的;
??2、禁止指令重排序 : 指令重排序是指CPU采用了允許將多條指令不按程序規(guī)定的順序分開發(fā)送給各相應(yīng)電路單元處理.
??分別用兩段常見代碼來闡述volatile這兩個(gè)特性 :

可見性(但不能保證原子性):
public class Instance {
    private static int i;
    public static void increase() {
        i++;
    }
}

??上面代碼實(shí)際要分為以下幾個(gè)步驟:

1. 將變量i讀取到工作內(nèi)存中;
2. int var = i + 1;操作賦值給var變量;
3. var變量再賦值給i;
4. i回寫到主內(nèi)存中;

??所以當(dāng)多個(gè)內(nèi)存同時(shí)調(diào)用increase()方法時(shí), 可能線程_1執(zhí)行到步驟2時(shí), 線程_2也執(zhí)行到步驟2, 就會(huì)導(dǎo)致increase()被兩個(gè)線程分別調(diào)用一次, 但是i實(shí)際為1的線程;

禁止指令重排序 :
public class Instance {
    private static Instance instance;
    public static Instance getInstance() {
        if (instance == null) {
            synchronized(Instance.class) {
                if (instance == null ) {
                    instance  = new Instance();
                }
            }
        }
        return instance;
    }
}

??instance = new Instance()可以分為以下幾個(gè)步驟 :

  • 1、棧內(nèi)存開辟空間給instance使用;
  • 2、堆內(nèi)存開辟空間準(zhǔn)備初始化對(duì)象;
  • 3、初始化對(duì)象;
  • 4、棧中引用指向這個(gè)堆內(nèi)存空間地址;

??但是由于指令重排序, 上述步驟也可能實(shí)際執(zhí)行順序?yàn)?_2_4_3;按照這個(gè)順序, 當(dāng)線程1執(zhí)行到步驟4時(shí), 線程2調(diào)用getInstance()方法發(fā)現(xiàn)instance此時(shí)已經(jīng)被賦值一個(gè)引用 != null, 直接走return方法, 但是此時(shí)instance所指向的Instance對(duì)象并沒有初始化完成, 即內(nèi)部變量都還是默認(rèn)值;

線程的實(shí)現(xiàn) :

實(shí)現(xiàn)線程主要有3種方式 : 使用內(nèi)核線程、使用用戶線程和使用用戶線程加輕量級(jí)進(jìn)程混合;
1、使用內(nèi)核線程實(shí)現(xiàn) :
??

線程的狀態(tài) :

??java語言定義了6種線程狀態(tài), 在任意一個(gè)時(shí)間點(diǎn), 一個(gè)線程只能有且僅有其中的一個(gè)狀態(tài):

  • 1、新建(New) : 創(chuàng)建后尚未啟動(dòng)的線程處于這種狀態(tài);
  • 2、運(yùn)行(Runnable) : Runnable包括了操作系統(tǒng)線程狀態(tài)中的Running和Ready, 也就是處于此狀態(tài)的線程有可能正在執(zhí)行, 也有可能正在等待著CPU為它分配執(zhí)行時(shí)間;
  • 3、無限期等待(Waiting) : 處于這種狀態(tài)的線程不會(huì)被分配CPU執(zhí)行時(shí)間, 它們要等待被其他線程顯示的喚醒. 以下方法會(huì)讓線程陷入無限期的等待狀態(tài);
1. 沒有設(shè)置Timeout參數(shù)的Object.wait()方法;
2. 沒有設(shè)置Timeout參數(shù)的Thread.join()方法;
3. LockSupport.park()方法;
  • 4、限期等待(Time Waiting) : 處于這種狀態(tài)的線程也不會(huì)被分配CPU執(zhí)行時(shí)間, 不過無須等待被其他線程顯示的喚醒, 在一定時(shí)間之后它們會(huì)由系統(tǒng)自動(dòng)喚醒. 以下方法會(huì)讓線程進(jìn)入限期等待狀態(tài):
1. Thread.sleep()方法;
2. 設(shè)置了Timeout參數(shù)的Object.wait()方法;
3. 設(shè)置了Timeout參數(shù)的Thread.join()方法;
4. LockSupport.parkNanos()方法;
5. LockSupport.parkUnitl()方法;
  • 5、阻塞(Blocked) : 線程被阻塞了, "阻塞狀態(tài)"與"等待狀態(tài)"的區(qū)別是: "阻塞狀態(tài)"在等待著獲取到一個(gè)排他鎖, 這個(gè)事件將在另外一個(gè)線程放棄這個(gè)鎖的時(shí)候發(fā)生; 而"等待狀態(tài)"則是在等待一段時(shí)間, 或者喚醒動(dòng)作的發(fā)生. 在程序等待進(jìn)入同步區(qū)域的時(shí)候, 線程將進(jìn)入這種狀態(tài);
  • 6、結(jié)束(Terminated) : 已終止線程的線程狀態(tài), 線程已經(jīng)結(jié)束執(zhí)行;
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 從三月份找實(shí)習(xí)到現(xiàn)在,面了一些公司,掛了不少,但最終還是拿到小米、百度、阿里、京東、新浪、CVTE、樂視家的研發(fā)崗...
    時(shí)芥藍(lán)閱讀 42,890評(píng)論 11 349
  • Java8張圖 11、字符串不變性 12、equals()方法、hashCode()方法的區(qū)別 13、...
    Miley_MOJIE閱讀 3,917評(píng)論 0 11
  • 突然記起來高三時(shí)候幾乎每天晚上都要去吃校門口對(duì)面的麻辣燙,永遠(yuǎn)不會(huì)忘記第一次點(diǎn)的時(shí)候點(diǎn)了18塊,這別的女生最多...
    寫文的詩琪閱讀 242評(píng)論 0 0
  • Time is the wealth of change, but the clock in its parody...
    我是嗚嗚閱讀 224評(píng)論 0 0
  • 臥在病床上的八十高齡的老人祝先生走到了他人生的盡頭?;赝簧臅r(shí)光就如同一瞬間,人活著就像放飛到空中的煙火,璀璨而...
    lk洛閱讀 702評(píng)論 7 9

友情鏈接更多精彩內(nèi)容