深入理解Java虚拟机——第十二章——Java内存模型与线程
硬件效率與一致性
處理器需要與內(nèi)存交互,但處理器運算速度與對內(nèi)存的I/O操作速度相差幾個數(shù)量級,因此現(xiàn)代操作系統(tǒng)不得不加入盡可能接近處理器運算速度的高速緩存來作為內(nèi)存與處理器之前的緩沖。這樣處理器就不用等待緩慢的內(nèi)存讀寫。
Java內(nèi)存模型
主存與工作內(nèi)存
Java內(nèi)存模型的主要目的是定義程序中各個變量的訪問規(guī)則。這里的變量和Java編程中的變量有所區(qū)別,這里指的是實例字段、靜態(tài)字段和構成數(shù)組對象的元素,不包括局部變量和方法參數(shù),因為是線程私有的,不會被共享,自然也不存在競爭問題。
Java內(nèi)存模型規(guī)定所有的變量都存儲在主內(nèi)存中。每條線程還有自己的工作內(nèi)存,線程的工作內(nèi)存存儲了被該線程使用到的變量的主內(nèi)存副本拷貝。變量的所有操作(讀取、賦值等)都必須在工作內(nèi)存中運行, 而不能直接讀寫主內(nèi)存中的變量。
內(nèi)存間交互操作
lock、unlock、read、load、use、assign、store、write。
read和load、store和write的操作必須是順序執(zhí)行,但可以不用連續(xù),比如read a、readb b、load b、load a。
?8種操作的規(guī)則:
- 一個變量在同一時刻只允許一條線程對其進行l(wèi)ock操作,但lock操作可以被同一線程執(zhí)行多次,多次lock后,只有執(zhí)行相同次數(shù)的unlock,變量才會被解鎖。
- 對一個變量lock,那么會清空工作內(nèi)存中該變量的值,需要重新load或assign這個值。
- 對一個變量unlock之前,必須將變量同步回主內(nèi)存
volatile變量的特殊規(guī)則
volatile可以說是最輕量級的同步機制。具備兩種特性:
volatie的讀和普通度的性能消耗沒什么區(qū)別,但是寫操作則可能會慢一些。
volatile對于操作的規(guī)則:
- read、load、use操作必須連續(xù)出現(xiàn),即保證每次都從主內(nèi)存讀取最新的值。
- assign、store、write操作必須連續(xù)出現(xiàn),即保證每次都把修改結果刷新到主內(nèi)存
64位的數(shù)據(jù)允許分成兩次32位的操作來執(zhí)行,但目前虛擬機都把其作為原子操作來處理,因此使用的時候不需要專門設置為volatile。
volatile的基本數(shù)據(jù)類型能解決不同線程對get/set方法的執(zhí)行,因為基本數(shù)據(jù)類型的操作是原子性的,即對其修改不依賴自身而是依賴傳入的值,因此執(zhí)行set方法不會說執(zhí)行到一半就切到另一個線程去get。
原子性、可見性、有序性
原子性:即操作的原子性,上述8個操作中除了lock和unlock外都是
可見性:當一個線程修改了共享變量值后,其它線程能立即得知這個修改。除了volatile外synchronized和final也能實現(xiàn)可見性。synchronized可見性是由“在unlock之前,必須把變量同步回主內(nèi)存”實現(xiàn)的。final是在構造器中一旦初始化完成,并且this引用沒有逃逸,那么在其它線程中就能看見final字段的值。
有序性:線程內(nèi)是有序的,線程之間是無序的。通過volatile和synchronized來提供線程之間有序。
Java與線程
線程的實現(xiàn)
Java在不同硬件和操作系統(tǒng)下對線程的操作統(tǒng)一處理
線程實現(xiàn)主要有3種方式:
Java線程調(diào)度
協(xié)同式線程調(diào)度和搶占式線程調(diào)度。
- 協(xié)同式線程調(diào)度:線程的執(zhí)行時間由線程本身控制,線程執(zhí)行完后,主動通知系統(tǒng)切換到另一個線程上。
- 搶占式線程調(diào)度:線程由系統(tǒng)來分配執(zhí)行時間,線程的切換不由線程本身決定(Thread.yield()可以讓出執(zhí)行時間,但是線程本身無法獲取執(zhí)行時間)。Java一共有10個級別的線程優(yōu)先級。Java線程是通過映射到系統(tǒng)的原生線程實現(xiàn)的,因此線程調(diào)度是取決于系統(tǒng)。
狀態(tài)轉(zhuǎn)換
Java定義了5種線程狀態(tài)(等待有兩種),在任意一個時間點,一個線程只能有一個狀態(tài)。
- 新建(new):創(chuàng)建后尚未啟動的線程
- 運行(runable):包括了操作系統(tǒng)中的running和ready,即處于此狀態(tài)中的線程有可能正在運行,也有可能在等待CPU為其分配執(zhí)行時間
- 等待: 無限期等待(waiting):處于這種狀態(tài)的線程不會被CPU分配時間,要等待被其它線程喚醒。以下方法會讓線程進入無限期等待:
- 沒有設置timeout的Object.wait()方法
- 沒有設置timeout的Thread.join()方法
- LockSupport.park()方法
- 設置timeout的Object.wait()方法
- 設置timeout的Thread.join()方法
- Thread.sleep()方法
- LockSupport.parkNanos()方法
- LockSupport.parkUntil()方法
- 阻塞(blocked):線程被阻塞,與等待的區(qū)別是,阻塞是等待獲取一個排他鎖,這個時間將在另一個線程放棄這個鎖的時候發(fā)生。等待狀態(tài)是等待一段時間或喚醒狀態(tài)發(fā)生。在程序進入同步區(qū)域的時候,線程進入這個狀態(tài)。
- 結束:已終止線程的狀態(tài),線程結束執(zhí)行。
?
轉(zhuǎn)載于:https://www.cnblogs.com/yjou/p/11265219.html
總結
以上是生活随笔為你收集整理的深入理解Java虚拟机——第十二章——Java内存模型与线程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: laravel 模型查询总结
- 下一篇: 《java基础知识》Java变量作用域