JVM系列(五)——Java線程


如果沒有多線程的出現,處理器的處理效率將會是很低的。試想象一個圖形化界面,如果只有一個線程,那麼每點擊一次界面的操作,特別是費時的操作,界面就需要一陣子的停頓,待後臺處理完後,才能夠繼續操作。這該是多差的用戶體驗啊。

Java提供了較爲成熟的內存模型,來支持多線程的運作。爲什麼需要好的內存模型呢?因爲在多線程中,最難以處理的就是各個變量的安全問題。例如,一個變量在某個線程中修改了,而另一個線程又讀取了舊數據並對其進行修改,就出現了“髒讀”的情況。一個好的內存模型既要考慮到變量、操作的安全性,又要兼顧效率,這就使之成爲一個棘手的問題了。

1、  Java的內存模型。

Java的內存模型主要目標是定義程序中各個變量的訪問規則。由於一些變量如:局部變量、方法參數,這些是由內存私有的,因此,不在討論的範疇。主要針對的是能夠被多個線程共用的變量,如:實例字段、靜態字段、構成數組對象的元素。因此,下文的變量,也是該意。

Java中,將內存分爲主內存與工作內存。

主內存中的變量是所有線程共享的,所有變量都是存儲在主內存當中的。

工作內存是每個線程所獨享的,他保存的是主內存中某些變量的拷貝,線程所有的操作都是在工作內存中,各個線程無法進行相互訪問,變量的傳遞需要通過主內存。


圖 線程、工作內存、主內存間的關係

 

2、  Volatile關鍵字

寫過多線程程序的話,應該對這個關鍵字並不陌生。他是一個輕量級的同步,與synchronized相比,所需的編碼和運行時的開銷都少。下面看看volatile變量的作用:

1)      作用一:保證此變量對所有線程的可見性。Volatile修飾的變量,在每次被線程訪問時,都強迫從共享內存中重讀該成員變量的值。而且,當成員變量發生變化時,強迫線程將變化值回寫到共享內存。這樣在任何時刻,兩個不同的線程總是看到某個成員變量的同一個值。

但是,以上的做法,並不能夠保證volatile修飾的變量是絕對線程安全的。因爲JVM只能保證在修改了volatile變量之後,將其寫回共享內存,但並不能夠保證從修改-寫回內存,這兩個操作是原子操作,因此,若另一個線程在變量還沒來得及寫回的情況下,就對其進行訪問,那麼讀到的數據便會是舊數據了。

對於普通變量,爲了獲得最佳速度,允許線程保存共享成員變量的私有拷貝,而且只當線程進入或者離開同步代碼塊時才與共享成員變量的原始值對比。

2)      作用二:禁止指令排序優化。JVM爲了能夠加快程序的執行速度,會對程序的指令進行排序優化。由於volatile修飾的變量可能會作爲其他線程的判斷符(如通過在對象初始化完畢之後賦值,用來判定某個對象是否初始化完畢),若該變量提前賦值,則會導致其他線程在對象未初始化完畢的情況下就進行對象使用,而導致出錯。

3)      對於volatile修飾的變量,就是提示VM:對於這個成員變量不能保存它的私有拷貝,而應直接與共享成員變量交互。在兩個或者更多的線程訪問的成員變量上使用volatile。當要訪問的變量已在synchronized代碼塊中,或者爲常量時,不必使用。

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