線程

        線程,有時被稱爲輕量進程,是程序執行流的最小單元,一個標準的線程由線程ID,當前指令指針(PC),寄存器集合和堆棧組成。另外,線程是進程中的一個實體,是被系統獨立調度和分派的基本單位,線程自己不擁有系統資源,只擁有一點在運行中必不可少的資源,但它可與同屬一個進程的其他線程共享進程所擁有的全部資源。一個線程可以創建和撤銷另一個線程,同一進程中的多個線程之間可以併發執行。由於線程之間的相互制約,致使線程在運行中呈現出間斷性。線程有就緒、阻塞、運行三種基本狀態。就緒是指線程具備運行的所有條件,邏輯上可以運行,在等待處理機;運行狀態是指線程佔有處理機正在運行;阻塞狀態是線程在等待一個事件(如:某個信號),邏輯上不可執行。每一個程序都至少有一個線程,若程序只有一個線程,那就是程序本身。

        線程是程序中一個單一的順序控制流程。進程內有一個相對獨立的、可調度的執行單元,是系統獨立調度和分派CPU的基本單位指令運行時的程序調度單位。在單個程序中同時運行多個線程完成不同的工作,稱爲:多線程。

     特點:

     1.輕量實體

       線程中的實體基本不擁有系統資源,只有一點必不可少的、能保證獨立運行的資源。

       線程的實體包括程序、數據和TCB。線程是動態概念,它的動態特性由線程控制塊TCB描述,TCB包括一下信息:

       1) 線程狀態   

       2) 當線程不運行時,被保存的現場資源

       3)一組執行堆棧

       4)存放每個線程的局部變量主存區

       5)訪問同一個進程中的主存和其他資源

     2.獨立分派和調度的基本單位

     3.可併發執行

     4.共享進程資源

 

線程與進程的區別:

     1.地址空間和其他資源:

        進程相互獨立,同一進程的各現線程間共享。某進程內的線程在其他進程不共享;

      2.通信:

        進程間通信IPC,線程間可以直接讀寫進程數據段(如:全局變量)來進行通信--需要進程同步和互斥手段的輔助,以保證數據的一致性。

      3.調度和切換:

        線程上下文切換比進程上下文切換要快得多。    

      4.在多線程OS中,線程不是一個可執行的實體。

  

線程體:

     JAVA 中線程體是從Thread類中集成run()方法,或實現Runnable接口的run()方法。 當線程產生並初始化後,實時系統調用它的run()方法。run()方法內的代碼實現所產生線程的行爲,它是線程的主要部分。

     一個應用程序可以通過使用線程中的方法setPriority(int),來設置線程的優先級大小。

     線程中的join()可以用來邀請其他線程先執行

 

幽靈線程

任何一個Java線程都能成爲幽靈線程。它是作爲運行於同一個進程內的對象和線程的服務提供者。例如,HotJava瀏覽器有一個稱爲" 後臺圖片閱讀器"的幽靈線程,它爲需要圖片的對象和線程從文件系統或網絡讀入圖片。 幽靈線程是應用中典型的獨立線程。它爲同一應用中的其他對象和線程提供服務。幽靈線程的run()方法一般都是無限循環,等待服務請求。

線程組

每個Java線程都是某個線程組的成員。線程組提供一種機制,使得多個線程集於一個對象內,能對它們實行整體操作。譬如,你能用一個方法調用來啓動或掛起組內的所有線程。Java線程組由ThreadGroup類實現。

當線程產生時,可以指定線程組或由實時系統將其放入某個缺省的線程組內。線程只能屬於一個線程組,並且當線程產生後不能改變它所屬的線程組。

 

多線程

多線程

對於多線程的好處這就不多說了。但是,它同樣也帶來了某些新的麻煩。只要在設計程序時特別小心留意,克服這些麻煩並不算太困難。在生成線程時必須將線程放在指定的線程組,也可以放在缺省的線程組中,缺省的就是生成該線程的線程所在的線程組。一旦一個線程加入了某個線程組,不能被移出這個組。

死鎖

如果程序中有幾個競爭資源的併發線程,那麼保證均衡是很重要的。系統均衡是指每個線程在執行過程中都能充分訪問有限的資源。系統中沒有餓死和死鎖的線程。Java並不提供對死鎖的檢測機制。對大多數的Java程序員來說防止死鎖是一種較好的選擇。最簡單的防止死鎖的方法是對競爭的資源引入序號,如果一個線程需要幾個資源,那麼它必須先得到小序號的資源,再申請大序號的資源。

優化

Java的多線程安全是基於Lock機制實現的,而Lock的性能往往不如人意。原因是,monitorenter與monitorexit這兩個控制多線程同步的bytecode原語,是JVM依賴操作系統互斥(mutex)來實現的。而互斥是一種會導致線程掛起,並在較短的時間內又需要重新調度回原線程的,較爲消耗資源的操作。所以需要進行對線程進行優化,提高效率。

輕量級鎖

輕量級鎖(Lightweight Locking)是從Java6開始引入的概念,本意是爲了減少多線程進入互斥的機率,並不是要替代互斥。它利用了CPU原語Compare-And-Swap(CAS,彙編指令CMPXCHG),嘗試在進入互斥前,進行補救。下面將詳細介紹JVM如何利用CAS,實現輕量級鎖。

mark word結構

Java Object Model中定義,Object Header是一個2字(1 word = 4 byte)長度的存儲區域。第一個字長度的區域用來標記同步,GC以及hash code等,官方稱之爲 mark word。第二個字長度的區域是指向到對象的Class。在2個word中,mark word是輕量級鎖實現的關鍵,其結構見右表。

聯繫流程圖

從表中可以看到,state爲lightweight locked的那行即爲輕量級鎖標記。bitfieds名爲指向lock record的指針,這裏的lock record,其實是一塊分配在線程堆棧上的空間區域。用於CAS前,拷貝object上的mark word。第三項是重量級鎖標記。後面的狀態單詞很有趣,inflated,譯爲膨脹,在這裏意思其實是鎖已升級到OS-level。一般我們只關注第二和第三項即可。lock,unlock與mark word之間的聯繫如右圖所示。在圖中,提到了拷貝object mark word,由於脫離了原始mark word,官方將它冠以displaced前綴,即displaced mark word(置換標記字)。這個displaced mark word是整個輕量級鎖實現的關鍵,在CAS中的compare就需要用它作爲條件。

交換指針

交換指針

在拷貝完object mark word之後,JVM做了一步交換指針的操作,即流程中第一個橙色矩形框內容所述。將object mark word裏的輕量級鎖指針指向lock record所在的stack指針,作用是讓其他線程知道,該object monitor已被佔用。lock record裏的owner指針指向object mark word的作用是爲了在接下里的運行過程中,識別哪個對象被鎖住了。

最後一步unlock中,我們發現,JVM同樣使用了CAS來驗證object mark word在持有鎖到釋放鎖之間,有無被其他線程訪問。如果其他線程在持有鎖這段時間裏,嘗試獲取過鎖,則可能自身被掛起,而mark word的重量級鎖指針也會被相應修改。此時,unlock後就需要喚醒被掛起的線程。

偏向鎖

Java偏向鎖(Biased Locking)是Java 6引入的一項多線程優化。它通過消除資源無競爭情況下的同步原語,進一步提高了程序的運行性能。它與輕量級鎖的區別在於,輕量級鎖是通過CAS來避免進入開銷較大的互斥操作,而偏向鎖是在無競爭場景下完全消除同步,連CAS也不執行(CAS本身仍舊是一種操作系統同步原語,始終要在JVM與OS之間來回,有一定的開銷)。所謂的無競爭場景,就是單線程訪問帶同步的資源或方法。

偏向鎖操作流程

偏向鎖,顧名思義,它會偏向於第一個訪問鎖的線程,如果在接下來的運行過程中,該鎖沒有被其他的線程訪問,則持有偏向鎖的線程將永遠不需要觸發同步。如果在運行過程中,遇到了其他線程搶佔鎖,則持有偏向鎖的線程會被掛起,JVM會嘗試消除它身上的偏向鎖,將鎖恢復到標準的輕量級鎖。(偏向鎖只能在單線程下起作用)。

偏向模式和非偏向模式,在mark word表中,主要體現在thread ID字段是否爲空。

掛起持有偏向鎖的線程,這步操作類似GC的pause,但不同之處是,它只掛起持有偏向鎖的線程(非當前線程)。

在搶佔模式的橙色區域說明中有提到,指向當前堆棧中最近的一個lock record(在輕量級鎖中,lock record是進入鎖前會在stack上創建的一份內存空間)。這裏提到的最近的一個lock record,其實就是當前鎖所在的stack frame上分配的lock record。整個步驟是從偏向鎖恢復到輕量級鎖的過程。

偏向鎖也會帶來額外開銷。在JDK6中,偏向鎖是默認啓用的。它提高了單線程訪問同步資源的性能。

但試想一下,如果你的同步資源或代碼一直都是多線程訪問的,那麼消除偏向鎖這一步驟對你來說就是多餘的。事實上,消除偏向鎖的開銷還是蠻大的。所以在你非常熟悉自己的代碼前提下,大可禁用偏向鎖 -XX:-UseBiasedLocking。

分類

線程有兩個基本類型

用戶級線程:管理過程全部由用戶程序完成,操作系統內核心只對進程進行管理。

系統級線程(核心級線程):由操作系統內核進行管理。操作系統內核給應用程序提供相應的系統調用應用程序接口API,以使用戶程序可以創建、執行、撤消線程。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章