java学生背景知识要求,好好学习Java并发 一、背景知识
并發指一個處理器同時處理多個任務,但這個定義只是從宏觀上來講的。舉個生活中的例子來說明,一個電商小二同時接待兩個客戶,小二通常的做法是在和A客戶聊的間隙回復B客戶的問題,和B客戶聊的間隙又回復A客戶的問題,這樣看起來小二同時服務了A和B,但是具體到某次答復,小二只是在為一個客戶服務。那么對應到計算機上,并發是怎樣發生的呢?
計算機的CPU通過給每個線程分配時間片來實現并發。時間片是CPU分配給各個線程的時間,每個線程被分配一個時間段,該線程在這個時間段擁有CPU的執行權,這個時間段就成為時間片。如果在時間片結束時線程還在運行,則CPU將被剝奪并分配給另一個線程。如果線程在時間片結束前阻塞或結束,則CPU當即進行切換,而不會造成CPU資源的浪費。這樣,宏觀上看有多個線程在并行進行;但是從微觀上看,由于只有一個CPU,一次只能處理一個線程線的一小段指令。
說到這里,似乎忽略了一個問題,為什么要引進并發呢?仍以上面的電商小二為例,在串行模式下小二先服務客戶A,等對客戶A的服務完成之后再對客戶B進行服務,這樣小二和客戶溝通中的時間空隙時間就白白流失掉了,串行模式的效率低于并發模式。另外并發模型更能滿足復雜業務的需要,因為并發模型能更好的模擬現實中的問題 。所以引進并發是很有必要的 。
接下來了解一些計算機的知識,為后面學習Java并發做鋪墊。
1. 高速緩存
計算機執行的任務大體上分為在CPU上計算和在內存上存取數據兩部分。然而計算機執行任務依賴的兩個硬件、CPU和內存在性能上差距有幾個數量級之大,而且短時間內內存性能的不足是彌補不了,于是工程師采用優化運行流程來解決問題。工程師在計算機系統加入一層的高速緩存(讀寫速度幾乎接近CPU的運算速度)來作為內存與CPU之間的緩沖。這樣可以將計算使用的數據復制到緩存中,CPU運行時直接操作緩存中的數據,當運算結束后再將數據從緩存同步回內存之中,這樣避免了CPU在運行的過程中長時間停止來等待從內存中讀寫數據了。
高速緩存彌補了CPU的執行速度和內存存取速度差距,但是也造成了新的問題:緩存一致性問題。在多核CPU系統中,每個核都有自己的高速緩存,同時這多個核共用一個主內存。這樣就可能發生一種情況:一個以上的CPU緩存了同一個主內存上的某個數據,CPU各自獨立的操作自己緩存的數據,結果主內存的某個數據在不同的CPU中的緩存值卻是不一樣的。
既然高速緩存方案已經廣泛使用了,緩存一致性問題也一定得到了解決。這個解決方案是要求處理器在訪問緩存時遵循特定的協議,這類型的協議有很多,如MESI協議:當CPU寫數據時,如果發現操作的變量是共享變量,即在其他CPU緩存中也存在該變量的副本,會發出信號通知它們將該變量的緩存行置為無效狀態,當其他CPU需要讀取這個變量時,發現自己緩存的該變量的副本是無效的,它就會從內存重新讀取。
仔細看MESI協議的原理,好像最終也沒能讓緩存保持一致,而是讓CPU及時發現緩存的不一致,并且讓不一致的緩存失效,有點兒兜底方案的感覺。
2. CPU的工作模式
一條計算機指令的執行一般可以可以分為以下幾個步驟:
取指 IF
譯碼和取寄存器操作數 ID
執行或者有效地址計算 EX
存儲器訪問 MEM
寫回 WB
這些步驟執行的過程中用到的是不同的硬件,比如,取指用到的是PC寄存器和存儲器,譯碼時用到的是指令寄存器組,執行時會使用ALU(算數邏輯單元)。基于這一特點,工程師發明了流水線技術來執行指令。這樣指令的執行順序將不再是串行的,不會因為一條指令卡在某個階段而影響后面指令的執行,只要CPU內部相應的處理部件未被占滿就可以不停的執行指令,這樣CPU的使用效率就會非常高。
但是,這也會帶來一個問題,后面執行的指令可能會比在它之前執行的指令完成的早,從而造成一種亂序,因為某些指令在EX階段耗時比較長的現象常有發生。這就是所謂的"順序流入,亂序流出"。
另外,現代處理器引入了寫緩存優化。處理器使用寫緩沖區臨時保存向內存寫入的數據,而不是直接將數據寫入內存,然后采用批處理的方式刷新寫緩沖區將數據批量寫入內存,而且會合并寫緩沖區中對同一內存地址的多次寫。這樣既可以可以保證指令流水線持續運行,同時也減少對內存總線的占用。
現在我們看另一種情況,有A、B、C、D四條依次執行的指令,其中B指令的執行需要依賴A的計算結果,這時候B指令就要停頓在某個階段,等待A執行完成后才能執行,C和D正常執行。想一想有沒有辦法縮短B指令執行過程中的停頓時間?肯定是有的,因為B和CD之間沒有關聯,可以把B放在C和D之后執行,這樣等B開始執行的時候說不定A已經執行完成了,CPU不需要停頓,這樣CPU的使用效率又有所提高。
事實上,現在很多語言也是這么做的,在編譯階段保證上下文前提的下會對指令進行重排,以提高CPU的處理性能。
3. 原子操作的實現原理
原子本意是"不能被進一步分割的最小粒子",而原子操作意為"不可被中斷的一個或一系列操作"。在多處理器上實現原子操作有點兒復雜,下面看下Intel處理器是如何實現原子操作。
對于基本的內存操作的原子性處理器會自動保證,否則并發的概念就將不復存在了。處理器保證從內存中讀取或者寫入一個字節是原子的,意思是當一個處理器讀取一個字節時,其他處理器不能訪問這個字節的內存地址。但是復雜內存操作的原子性需要借助總線鎖定和緩存鎖定兩個機制來保證。
3.1 使用總線鎖保證復雜操作原子性
所謂總線鎖就是使用處理器提供的一個LOCK#信號,當一個處理器在總線上輸出此信號時,其他處理器的請求將被阻塞,那么該處理器就可以獨占共享內存(聽到鎖是不是很親切,很熟悉)。感覺和處理器自動保證基本內存操作原子性的原理差不多,都是排斥其他處理器同時操作,只不過前者獨占的是某個字節的內存地址,而總線鎖獨占的是整個內存。
獨占整個內存聽著就很浪費,沒有代價小點兒的方法嗎?當然有,緩存鎖定鎖定機制就是。
3.2 使用緩存鎖定來保證原子性
在上面高速緩存的知識里面,我們知道每個處理器都有一套高速緩存用于緩存頻繁操作的內存,處理器不直接操作主內存的地址,而是操作這些緩存,操作完成之后再將緩存數據寫回主內存。在這種模式下,只要保證緩存寫回的準確就可以保證該操作的原子性。這也就是緩存鎖定保證復雜操作的原子性的原理。下面是規范的描述:
內存區域如果被緩存在處理的緩存行中,并且在Lock操作期間被鎖定,那么當它執行鎖操作回寫到內存時,處理器不在總線上聲言LOCK#信號,而是修改內部的內存地址,并允許它的緩存一致性機制來保證操作的原子性,因為緩存一致性機制會阻止同時修改由兩個以上處理器緩存的內存區域數據,當其他處理器回寫已經被鎖定的緩存行數據時,會使自己的緩存行無效。
現又有一個問題了,既然有低損耗緩存鎖定機制,為什么還要用總線鎖定機制?這是因為有以下兩種情況不能使用緩存鎖定:
第一種情況是:當操作的數據不能被緩存在處理器內部,或者操作的數據跨越了多個緩存行時。
第二種情況是:有些處理器不支持緩存鎖定,比如Intel 486 和Pentium 處理器。
上面這些計算機硬件知識都是和并發相關的,因為Java程序也是在計算機上運行的,那么Java并發應該也是基于計算機的并發特點做的設計,了解了這些知識,后面可以比較容易的理解Java并發的知識。
參考文獻 《Java并發編程的藝術》
總結
以上是生活随笔為你收集整理的java学生背景知识要求,好好学习Java并发 一、背景知识的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: kmp算法详解php,php中字符串匹配
- 下一篇: hashmap应用场景_Java初学者进