1. 程式人生 > >多執行緒理解(一)  三大特性

多執行緒理解(一)  三大特性

程序、執行緒和多執行緒的定義。

  1. 程序:是指一段具有某種獨立功能的程式關於某個資料集合的一次執行活動(一段程式的執行過程)。
  2. 執行緒:單個程序執行中的每個任務就是一個執行緒。
  3. 多執行緒:在一個程序中有多個執行緒共享程序資源,執行任務。

多執行緒的三大特性:

  1. 原子性:執行緒的一個或者多個操作要麼全部執行,而且執行過程不會被打斷,要麼全部都不執行。
  2. 可見性:可見性是指多個執行緒訪問同一個變數的時候,一個執行緒修改了這個變數的值,其他執行緒也可以立刻看到這個修改後的值。Java提供關鍵字volatile關鍵字來保證可見性(當一個共享變數被volatile修飾後,它會保證修改的值立即更新到主存中,其他執行緒如果需要用到這個變數,會到主存中去取,這樣保證每個執行緒讀取到的資料都是最新的)。Synchronized和Lock也能保證可見性,Synchronized和Lock保證同一時刻只有一個執行緒能夠獲得鎖然後執行同步程式碼,並且在釋放鎖之前,將變數的值重新整理到主存中,因此可以保證可見性。
  3. 有序性:即程式的執行順序按照程式碼的先後順序執行。(在java記憶體模型中,允許編譯器和處理器對指令進行重排序,重排序過程不會影響單執行緒程式的執行,但是會影響多執行緒併發執行的正確性)

舉個栗子解釋指令重排序:

對於第5行,可分成三行虛擬碼

1 memory=allocate();// 分配記憶體 相當於c的malloc

2 ctorInstanc(memory) //初始化物件

3 instance=memory //設定instance指向剛分配的地址

上面的程式碼在編譯器執行時,可能會出現重排序 從1-2-3 排序為1-3-2

如此在多執行緒下就會出現問題

例如現在有2個執行緒A,B

執行緒A在執行第5行程式碼時,B執行緒進來,而此時A執行了 1和3,沒有執行2,此時B執行緒判斷instance不為null 直接返回一個未初始化的物件,就會出現問題

而用了volatile,上面的重排序就會在多執行緒環境中禁止,不會出現上述問題

在JAVA 裡面,可以通過volatile關鍵字來保證“一定的”有序性,另外也可以通過synchronized和lock來保證程式碼的有序性。因為synchronized和lock保證同一時間只有一個執行緒執行一段程式,相當於單執行緒的執行,所以能夠保證有序性。

此外,JAVA的記憶體模型具有一些先天的“有序性”,既不需要通過任何手段就能夠保證有序性,這個通常也稱為 happens-before 原則。如果兩個操作的執行順序沒法從happens-before原則推匯出來,那麼就不能保證它們的有序性,虛擬機器可以隨意將他們進行重排序。

下面就來具體介紹下happens-before原則(先行發生原則):

  • 程式次序規則:一個執行緒內,按照程式碼順序,書寫在前面的操作先行發生於書寫在後面的操作
  • 鎖定規則:一個unLock操作先行發生於後面對同一個鎖額lock操作
  • volatile變數規則:對一個變數的寫操作先行發生於後面對這個變數的讀操作
  • 傳遞規則:如果操作A先行發生於操作B,而操作B又先行發生於操作C,則可以得出操作A先行發生於操作C
  • 執行緒啟動規則:Thread物件的start()方法先行發生於此執行緒的每個一個動作
  • 執行緒中斷規則:對執行緒interrupt()方法的呼叫先行發生於被中斷執行緒的程式碼檢測到中斷事件的發生
  • 執行緒終結規則:執行緒中所有的操作都先行發生於執行緒的終止檢測,我們可以通過Thread.join()方法結束、Thread.isAlive()的返回值手段檢測到執行緒已經終止執行
  • 物件終結規則:一個物件的初始化完成先行發生於他的finalize()方法的開始

 這8條原則摘自《深入理解Java虛擬機器》。