国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

java內(nèi)存模型和線程

shiyang6017 / 2544人閱讀

摘要:內(nèi)存之間的交互關(guān)于主內(nèi)存和工作內(nèi)存之間的具體交互協(xié)議,內(nèi)存模型定義了中操作來完成,虛擬機實現(xiàn)的時候必須保證每個操作都是原子的,不可分割的對于和有例外鎖定作用于主內(nèi)存變量,代表一個變量是一條線程獨占。

并發(fā)不一定依賴多線程,但是在java里面談論并發(fā),大多與線程脫不開關(guān)系。

線程是大多是面試都會問到的問題。我們都知道,線程是比進程更輕量級的調(diào)度單位,線程之間可以共享內(nèi)存。之前面試的時候,也是這樣回答,迷迷糊糊,沒有一個清晰的概念。

大學的學習的時候,寫C和C++,自己都沒有用過多線程,看過一個Windows編程的書,里面講多線程的時候,一大堆大寫的字母,看著一點都不爽,也是慚愧。后來的實習,寫unity,unity的C#使用的是協(xié)程。只有在做了java后端之后,才知道線程到底是怎么用的。了解了java內(nèi)存模型之后,仔細看了一些資料,對java線程有了更深入的認識,整理寫成這篇文章,用來以后參考。

1 Java內(nèi)存模型

Java虛擬機規(guī)范試圖定義一種java內(nèi)存模型來屏蔽掉各種硬件和操作系統(tǒng)的內(nèi)存訪問差異,以實現(xiàn)讓java程序在各種平臺下都能達到一致性內(nèi)存訪問的效果。

java內(nèi)存模型的主要目標是定義程序中各個變量的訪問規(guī)則,即在虛擬機中將變量存儲到內(nèi)存和從內(nèi)存中取出變量的底層細節(jié)。(這里所說的變量包括了實例字段、靜態(tài)字段和數(shù)組等,但不包括局部變量與方法參數(shù),因為這些是線程私有的,不被共享。)

1.1 主內(nèi)存和工作內(nèi)存

java規(guī)定所有的變量都存儲在主內(nèi)存。每條線程有自己的工作內(nèi)存

線程的工作內(nèi)存中的變量是主內(nèi)存中該變量的副本,線程對變量的所有操作都必須在工作內(nèi)存中進行,而不能直接讀寫主內(nèi)存中的變量。不同線程間也無法直接訪問對方工作內(nèi)存中的變量,線程間變量值的傳遞需要通過主內(nèi)存來完成。

1.2 內(nèi)存之間的交互

關(guān)于主內(nèi)存和工作內(nèi)存之間的具體交互協(xié)議,java內(nèi)存模型定義了8中操作來完成,虛擬機實現(xiàn)的時候必須保證每個操作都是原子的,不可分割的(對于long和double有例外)

lock鎖定:作用于主內(nèi)存變量,代表一個變量是一條線程獨占。

unlock解鎖:作用于主內(nèi)存變量,把鎖定的變量解鎖。

read讀取:作用于主內(nèi)存變量,把變量值從主內(nèi)存?zhèn)鞯骄€程的工作內(nèi)存中,供load使用。

load載入:作用工作內(nèi)存變量,把上一個read到的值放入到工作內(nèi)存中的變量中。

use使用:作用于工作內(nèi)存變量,把工作內(nèi)存中的一個變量的值傳遞給執(zhí)行引擎。

assign:作用于工作內(nèi)存變量,把執(zhí)行引擎執(zhí)行過的值賦給工作內(nèi)存中的變量。

store存儲:作用于工作內(nèi)存變量,把工作內(nèi)存中的變量值傳給主內(nèi)存,供write使用。

這些操作要滿足一定的規(guī)則。

1.3 volatile

volatile可以說是java的最輕量級的同步機制。

當一個變量被定義為volatile之后,他就具備兩種特性:

保證此變量對所有線程都是可見的

這里的可見性是指當一個線程修改了某變量的值,新值對于其他線程來講是立即得知的。而普通變量做不到,因為普通變量需要傳遞到主內(nèi)存中才可以做到這點。

禁止指令重排

對于普通變量來說,僅僅會保證在該方法的執(zhí)行過程中所有依賴賦值結(jié)果的地方都能獲取到正確的結(jié)果,而不能保證變量賦值操作的順序與程序代碼中的執(zhí)性順序一致。

若用volatile修飾變量,在編譯時,會在指令序列中插入內(nèi)存屏障來禁止特定類型的處理器重排序。

volatile對于單個的共享變量的讀/寫具有原子性,但是像num++這種復合操作,volatile無法保證其原子性。

1.4 long和double

long和double是一個64位的數(shù)據(jù)類型。

虛擬機允許將沒有被volatile修飾的64位變量的讀寫操作分為兩次32位的操作來進行。因此當多個線程操作一個沒有聲明為volatile的long或者double變量,可能出現(xiàn)操作半個變量的情況。

但是這種情況是罕見的,一般商用的虛擬機都是講long和double的讀寫當成原子操作進行的,所以在寫代碼時不需要將long和double專門聲明為volatile。

1.5 原子性、可見性和有序性

java的內(nèi)存模型是圍繞著在并發(fā)過程中如何處理原子性、可見性和有序性。

原子性

基本數(shù)據(jù)類型的訪問讀寫是劇本原子性的。

如果需要一個更大范圍的原子性保證,java提供了lock和unlock操作,對應于寫代碼時就是synchronized關(guān)鍵字,因此在synchronized塊之間的操作也是具備原子性的。

可見性

可見性是指當一個線程修改到了一個共享變量的值,其他的線程能夠立即得知這個修改。共享變量的讀寫都是通過主內(nèi)存作為媒介來處理可見性的。

volatile的特殊規(guī)則保證了新值可以立即同步到主內(nèi)存,每次使用前立即從主內(nèi)存刷新。

synchronized同步塊的可見性是由”對于一個變量unlock操作之前,必須先把此變量同步回內(nèi)存中“來實現(xiàn)的。

final的可見性是指被final修飾的字段在構(gòu)造器中一旦初始化完成,并且構(gòu)造器沒有把this的引用傳遞出去,那么在其他線程中就能看見final字段的值。

有序性

如果在本線程內(nèi)觀察,所有的操作都是有序的;如果在一個線程內(nèi)觀察另一個線程,所有的操作都是無序的。
volatile關(guān)鍵字本身就包含了禁止指令重排的語義,而synchronized則是由“一個變量在同一時刻只允許一條線程對其進行l(wèi)ock操作”這條規(guī)則來實現(xiàn)有序性的。

1.6 先行發(fā)生原則

如果java內(nèi)存模型中的所有有序性都是靠著volatile和synchronized來完成,那有些操作將會變得很繁瑣,但是我們在寫java并發(fā)代碼的時候沒有感受到這一點,都是因為java有一個“先行發(fā)生”原則。

先行發(fā)生是java內(nèi)存模型中定義的兩項操作之間的偏序關(guān)系,如果說操作A先發(fā)生于操作B,其實就是說在發(fā)生B之前,A產(chǎn)生的影響都能被B觀察到,這里的影響包括修改了內(nèi)存中共享變量的值、發(fā)送了消息、調(diào)用了方法等等。

程序次序規(guī)則

在一個線程內(nèi),按程序代碼控制流順序執(zhí)行。

管程鎖定規(guī)則

unlock發(fā)生在后面時間同一個鎖的lock操作。

volatile變量規(guī)則

volatile變量的寫操作發(fā)生在后面時間的讀操作。

線程啟動規(guī)則

線程終止規(guī)則

線程中斷規(guī)則

對象終結(jié)規(guī)則

一個對象的初始化完成在finalize方法之前。

傳遞性

如果A先行發(fā)生B,B先行發(fā)生C,那么A先行發(fā)生C。

由于指令重排的原因,所以一個操作的時間上的先發(fā)生,不代表這個操作就是先行發(fā)生;同樣一個操作的先行發(fā)生,也不代表這個操作必定在時間上先發(fā)生。

2 Java線程 2.1 線程的實現(xiàn)

主流的操作系統(tǒng)都提供了線程的實現(xiàn),java則是在不同的硬件和操作系統(tǒng)的平臺下,對線程的操作提供了統(tǒng)一的處理,一個Thread類的實例就代表了一個線程。Thread類的關(guān)鍵方法都是native的,所以java的線程實現(xiàn)也都是依賴于平臺相關(guān)的技術(shù)手段來實現(xiàn)的。

實現(xiàn)線程主要有3種方式:使用內(nèi)核線程實現(xiàn),使用用戶線程實現(xiàn)和使用用戶線程加輕量級進程實現(xiàn)。

2.1.1 使用內(nèi)核線程實現(xiàn)

內(nèi)核線程就是直接由操作系統(tǒng)內(nèi)核支持的線程,這種線程由內(nèi)核來完成線程的切換,內(nèi)核通過操縱調(diào)度器對線程進行調(diào)度,并負責將線程的任務映射到各個處理器上。

程序一般不會直接去調(diào)用內(nèi)核線程,而是使用內(nèi)核線程的一個高級接口——輕量級進程(Light Weigh Process),LWP就是我們通常意義上所說的線程。

由于每個輕量級進程都由一個內(nèi)核線程支持,這種輕量級進程與內(nèi)核線程之間1:1的關(guān)系成為一對一線程模型。

局限性

雖然由于內(nèi)核線程的支持,每個輕量級進程都成為了一個獨立的調(diào)度單元,即使有一個阻塞,也不影響整個進程的工作,但是還是有一定的局限性:

系統(tǒng)調(diào)用代價較高

由于基于內(nèi)核線程實現(xiàn),所以各種線程的操作都要進行系統(tǒng)調(diào)用。而系統(tǒng)調(diào)用的代價比較高,需要在用戶態(tài)和內(nèi)核態(tài)來回切換。

系統(tǒng)支持數(shù)量有限

每個輕量級進程都需要一個內(nèi)核線程支持,需要消耗一定的內(nèi)核資源,所以支持的線程數(shù)量是有限的。

2.1.2 使用用戶線程實現(xiàn)

指的是完全建立在用戶空間的線程庫上,系統(tǒng)內(nèi)核不能感知線程存在的實現(xiàn)。用戶線程的建立、同布、銷毀和調(diào)度完全在用戶態(tài)中完成,不需要內(nèi)核幫助。

如果程序?qū)崿F(xiàn)得當,則這些線程都不需要切換到內(nèi)核態(tài),操作非常快速消耗低,可以支持大規(guī)模線程數(shù)量。這種進程和用戶線程之間1:N的關(guān)系成為一對多線程模型。

局限性

不需要系統(tǒng)內(nèi)核的,既是優(yōu)勢也是劣勢。由于沒有系統(tǒng)內(nèi)核支援,所有的操作都需要程序去處理,由于操作系統(tǒng)只是把處理器資源分給進程,那“阻塞如何處理”、“多處理器系統(tǒng)如何將線程映射到其他處理器上”這類問題的解決十分困難,所以現(xiàn)在使用用戶線程的越來越少了。

2.1.3 使用用戶線程加輕量級進程混合實現(xiàn)

在這種混合模式下,既存在用戶線程,也存在輕量級進程。

用戶線程還是完全建立在用戶空間中,因此用戶線程的創(chuàng)建、切換、析構(gòu)等操作依然廉價,而且支持大規(guī)模用戶線程并發(fā)、而操作系統(tǒng)提供支持的輕量級進程則作為用戶線程和內(nèi)核線程之間的橋梁,這樣可以使用內(nèi)核提供的線程調(diào)度和處理器映射,并且用戶線程的系統(tǒng)調(diào)用要通過輕量級進程來完成,大大降低了整個進程被完全阻塞的風險。

在這種模式下,用戶線程和輕量級進程數(shù)量比不固定N:M,這種模式就是多對多線程模型。

2.1.4 java線程的實現(xiàn)

目前的jdk版本中,操作系統(tǒng)支持怎樣的線程模型,很大程度上就決定了jvm的線程是怎么映射的,這點在不同的平臺沒辦法打成一致。線程模型只對線程的并發(fā)規(guī)模和操作成本產(chǎn)生影響,對編碼和運行都沒什么差異。

windows和linux都是一對一的線程模型。

2.2 線程調(diào)度

線程的調(diào)度是指系統(tǒng)為線程分配處理器使用權(quán)的過程,主要的調(diào)度方式有兩種:協(xié)同式線程調(diào)度和搶占式線程調(diào)度。

2.2.1 協(xié)同式線程調(diào)度

線程的執(zhí)性時間由線程本身來控制,線程把自己的工作執(zhí)性完了之后,要主動通知系統(tǒng)切換到另外一個線程上。Lua的協(xié)程就是這樣。

好處

協(xié)同式多線程最大的好處就是實現(xiàn)簡單。

由于線程要把自己的事情干完之后才進行線程切換,切換操作對線程是克制的,所以沒有什么線程同步的問題。

壞處

壞處也很明顯,線程執(zhí)行時間不可控。甚至如果一個線程寫的問題,一直不告訴系統(tǒng)切換,那程序就會一直阻塞。

2.2.2 搶占式線程調(diào)度

每個線程由系統(tǒng)分配執(zhí)行時間,線程的切換不是又線程本身來決定。

使用yield方法是可以讓出執(zhí)行時間,但是要獲取執(zhí)行時間,線程本身是沒有什么辦法的。

在這種調(diào)度模式下,線程的執(zhí)行時間是系統(tǒng)可控的,也就不會出現(xiàn)一個線程導致整個進程阻塞。

2.2.3 java線程調(diào)度

java使用的是搶占式線程調(diào)度。

雖然java的線程調(diào)度是系統(tǒng)來控制的,但是可以通過設置線程優(yōu)先級的方式,讓某些線程多分配一些時間,某些線程少分配一些時間。

不過線程優(yōu)先級還是不太靠譜,原因就是java的線程是通過映射到系統(tǒng)的原生線程來實現(xiàn)的,所以線程的調(diào)度還是取決于操作系統(tǒng),操作系統(tǒng)的線程優(yōu)先級不一定和java的線程優(yōu)先級一一對應。而且優(yōu)先級還可能被系統(tǒng)自行改變。所以我們不能在程序中通過優(yōu)先級來準確的判斷先執(zhí)行哪一個線程。

2.3 線程的狀態(tài)轉(zhuǎn)換

看到網(wǎng)上有好多種說法,不過大致也都是說5種狀態(tài):新建(new)、可運行(runnable)、運行(running)、阻塞(blocked)和死亡(dead)。

而深入理解jvm虛擬機中說java定義了5種線程狀態(tài),在任一時間點,一個線程只能有其中的一種狀態(tài):

新建new

運行runnable

包括了操作系統(tǒng)線程狀態(tài)的running和ready,也就是說處于此狀態(tài)的線程可能正在執(zhí)行,也可能正在等待cpu給分配執(zhí)行時間。

無限期等待waiting

處于這種狀態(tài)的線程不會被cpu分配執(zhí)行時間,需要被其他線程顯示喚醒,能夠?qū)е戮€程陷入無限期等待的方法有:

沒有設置timeout參數(shù)的wait方法。

沒有設置timeout參數(shù)的join方法。

LockSupport.park方法。

限期等待timed waiting

處于這種狀態(tài)的線程也不會被cpu分配執(zhí)行時間,不過不需要被其他線程顯示喚醒,是經(jīng)過一段時間之后,被操作系統(tǒng)自動喚醒。能夠?qū)е戮€程陷入限期等待的方法有:

sleep方法。

設置timeout參數(shù)的wait方法。

設置參數(shù)的join方法。

LockSupport.parkNanos方法。

LockSupport.parkUntil方法。

阻塞blocked

線程被阻塞了。在線程等待進入同步區(qū)域的時候是這個狀態(tài)。

阻塞和等待的區(qū)別是:阻塞是排隊等待獲取一個排他鎖,而等待是指等一段時間或者一個喚醒動作。

結(jié)束terminated

已經(jīng)終止的線程。

3 寫在最后

并發(fā)處理的廣泛應用是使得Amdahl定律代替摩爾定律成為計算機性能發(fā)展源動力的根本原因,也是人類壓榨計算機運算能力的最有力武器。有些問題使用越多的資源就能越快地解決——越多的工人參與收割莊稼,那么就能越快地完成收獲。但是另一些任務根本就是串行化的——增加更多的工人根本不可能提高收割速度。

我們使用線程的重要原因之一是為了支配多處理器的能力,我們必須保證問題被恰當?shù)剡M行了并行化的分解,并且我們的程序有效地使用了這種并行的潛能。有時候良好的設計原則不得不向現(xiàn)實做出一些讓步,我們必須讓計算機正確無誤的運行,首先保證并發(fā)的正確性,才能夠在此基礎上談高效,所以線程的安全問題是一個很值得考慮的問題。

雖然一直說java不好,但是java帶給我的影響確實最大的,從java這個平臺里學到了很多有用的東西。現(xiàn)在golang,nodejs,python等語言,每個都是在一方面能秒java,可是java生態(tài)和java對軟件行業(yè)的影響,是無法被超越的,java這種語言,從出生到現(xiàn)在幾十年了,基本上每次軟件技術(shù)的革命都沒有落下,每次都覺得要死的時候,忽然間柳暗花明,枯木逢春。咳咳,扯遠了。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/70694.html

相關(guān)文章

  • 簡述Java內(nèi)存模型

    摘要:內(nèi)存模型即,簡稱,其規(guī)范了虛擬機與計算機內(nèi)存時如何協(xié)同工作的,規(guī)定了一個線程如何和何時看到其他線程修改過的值,以及在必須時,如何同步訪問共享變量。內(nèi)存模型要求調(diào)用棧和本地變量存放在線程棧上,對象存放在堆上。 Java內(nèi)存模型即Java Memory Model,簡稱JMM,其規(guī)范了Java虛擬機與計算機內(nèi)存時如何協(xié)同工作的,規(guī)定了一個線程如何和何時看到其他線程修改過的值,以及在必須時,...

    ACb0y 評論0 收藏0
  • 來,了解一下Java內(nèi)存模型(JMM)

    摘要:因為管理人員是了解手下的人員以及自己負責的事情的。處理器優(yōu)化和指令重排上面提到在在和主存之間增加緩存,在多線程場景下會存在緩存一致性問題。有沒有發(fā)現(xiàn),緩存一致性問題其實就是可見性問題。 網(wǎng)上有很多關(guān)于Java內(nèi)存模型的文章,在《深入理解Java虛擬機》和《Java并發(fā)編程的藝術(shù)》等書中也都有關(guān)于這個知識點的介紹。但是,很多人讀完之后還是搞不清楚,甚至有的人說自己更懵了。本文,就來整體的...

    kviccn 評論0 收藏0
  • 來,了解一下Java內(nèi)存模型(JMM)

    摘要:因為管理人員是了解手下的人員以及自己負責的事情的。處理器優(yōu)化和指令重排上面提到在在和主存之間增加緩存,在多線程場景下會存在緩存一致性問題。有沒有發(fā)現(xiàn),緩存一致性問題其實就是可見性問題。 網(wǎng)上有很多關(guān)于Java內(nèi)存模型的文章,在《深入理解Java虛擬機》和《Java并發(fā)編程的藝術(shù)》等書中也都有關(guān)于這個知識點的介紹。但是,很多人讀完之后還是搞不清楚,甚至有的人說自己更懵了。本文,就來整體的...

    eccozhou 評論0 收藏0
  • Java內(nèi)存模型

    摘要:內(nèi)存模型指定了如何與計算機內(nèi)存協(xié)同工作。內(nèi)部的內(nèi)存模型內(nèi)存模型在內(nèi)部使用,將內(nèi)存分為了線程棧和堆。下面的圖從邏輯角度給出了內(nèi)存模型每個運行在內(nèi)部的線程都有自己的線程棧。部分線程棧和堆可能在某些時候會占用緩存和內(nèi)部寄存器。 Java內(nèi)存模型指定了JVM如何與計算機內(nèi)存協(xié)同工作。JVM是整個計算機的模型因此這個模型包含了內(nèi)存模型,也就是Java內(nèi)存模型。 如果你像要設計正確行為的并發(fā)程序,...

    fantix 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<