摘要:在多線程的問題上面概念比較多,也需要慢慢理解,其實也在多線程的鎖的上面做了很多優(yōu)化,還有互斥同步和非互斥同步,還有很多概念,什么是自旋和自適應自旋,鎖消除順便提一下,上面的字符串拼接的例子就是用到了這種優(yōu)化方式,鎖粗化,我們下次再繼續(xù)分享。
在我們平常的開發(fā)工作中,或多或少的都能接觸到多線程編程或者一些并發(fā)問題,隨著操作系統(tǒng)和系統(tǒng)硬件的升級,并發(fā)編程被越來越多的運用到我們的開發(fā)中,我們使用多線程的最初的想法是能夠更大程度的利用系統(tǒng)資源,但是我們在使用多線程的時候,也會有一些問題的存在,我們先來看一段代碼。
private static int i = 0; private static void increse(){ i++; } public static void main(String[] args) { Thread[] threads = new Thread[20]; for (int i = 0; i < threads.length; i++){ threads[i] = new Thread(new Runnable() { @Override public void run() { for (int j = 0; j < 10000; j++){ increse(); } } }); threads[i].start(); } while (Thread.activeCount() > 1){ Thread.yield(); } System.out.println(i); }
首先看看這段代碼是沒有問題的,但是如果在多線程的環(huán)境中,這段代碼運行的結(jié)果基本是都不一樣的,這里是開啟20個線程,然后每一個線程調(diào)用increse()方法對變量i進行一個賦值操作,預期的一個輸出應該是200000,但是為什么會每一次的輸出都不太一樣呢?原因就在于這個地方i++,這里就是產(chǎn)生并發(fā)問題的根本原因,那看起來很簡單的一個i++為什么會有這種問題?這里就簡單的從java內(nèi)存模型(JMM)來了解一下,首先JMM規(guī)定,每一個線程運行時都會有一個工作內(nèi)存,然后變量i是存儲在主內(nèi)存的,每次線程在計算數(shù)據(jù)的時候都要去主內(nèi)存中獲取當前變量的值,那么簡單的來說,就是一個線程將變量i計算得到結(jié)果后,還沒有將這個數(shù)據(jù)刷新到主內(nèi)存,在這個時候,其他的線程已經(jīng)獲取到了原來的值,換句話說,本線程中獲取到的數(shù)據(jù)是否是最新的,這個是不知道的。但是這只是從JMM角度來簡單的說一下,i++這個看似簡單的操作其實包含了三個操作,獲取i的值,對i進行自增,然后對i進行賦值。就是在這幾個操作中,其他的線程會有很多的時間來做很多的事情,那有人會問,是不是將這個i++操作同步就可以了?是的,那該如何同步呢?
有人說用volatile來修飾一下變量i不就可以了么?是的,java中volatile這個關(guān)鍵字確實是提供了一個同步的功能,但是為什么在這里修改一下還是沒有效果呢?原因就在于如果一個變量用volatile修飾之后,只是會讓其他的線程立即能夠知道當前變量的值是多少,這里就叫做可見性,但是還是解決不了i++這幾個操作的問題,那如何處理,又有人提出用synchronized這個關(guān)鍵字,不得不承認,這個關(guān)鍵字確實很強大,是能夠解決這個問題,那我們有沒有考慮過這個為什么可以解決這個問題,是如何解決的,那還是簡單的說一下,首先synchronized是屬于JVM級別的,有這個關(guān)鍵字的方法或者代碼塊,最后會被解釋成monitorenter和monitorexit指令,這兩個字節(jié)碼都明確需要一個reference類型的參數(shù)來指出要鎖定或者解鎖的對象,像這樣:
public synchronized String f(){ //code } synchronized(object){ //code }
看到這里,我們應該能明白synchronized為什么可以解決上面程序的問題,但是我們還應該要明確一個概念就是原子性,換句話說,就是我們在處理一些多線程的問題的時候,應該保證一些共享數(shù)據(jù)的操作是原子性的,這樣才能保證正確性,看到這里,相信你也有了一個大概的理解,那我們來總結(jié)一下,在處理多線程的問題的時候,哪些點是值得注意的,可見性,原子性,有序性,這幾個點是保證多線程能夠正確的一個前提條件,至于什么是有序性,這里涉及到內(nèi)存指令的重排序,不在討論范圍內(nèi),以后再來討論。
這里還要指出一個問題,就是是否我們在處理多線程問題的時候,一定要同步,或者說一定要加鎖,這個也不是一定的,之前網(wǎng)上有一個說笑的方式,就是我們在處理多線程的問題的時候,有時候就會發(fā)現(xiàn),代碼又被寫成了單線程,當然這只是一個玩笑話,但是這里我們也能看出來,是不是單線程的程序就不會有這些問題?答案是肯定的,因為單線程不存在資源競爭的問題,也就不需要再討論了。
那么我們什么時候需要使用同步,什么時候又不需要呢?我們來看一段代碼
public String f(String s1, String s2, String s3){ return s1 + s2 +s3; }
這是一個字符串拼接的一個方法,我們來反編譯看一下,這里JVM到底是怎么做的?
這里很明顯的能夠看出來,最后是通過StringBuilder來為我們生成了最后的結(jié)果,那有人會問,這里線程安全么?是的,這里是線程安全的,因為在這個方法中,雖然也有變量的使用,但是都是屬于線程內(nèi)部在使用,其他的線程根本不會訪問到或者說這些變量也不會讓其他線程訪問到,我們稱其為沒有方法逃逸,也就是說只能在本線程中使用這些變量,這里是線程安全的,至于什么是逃逸分析,簡單的提一下就是這是JVM的高級優(yōu)化的一種方式,說的再簡單一點,就是別的線程訪問不到這個變量,這樣的代碼是不需要同步的。
在多線程的問題上面概念比較多,也需要慢慢理解,其實JVM也在多線程的鎖的上面做了很多優(yōu)化,還有互斥同步和非互斥同步,還有很多概念,什么是自旋和自適應自旋,鎖消除(順便提一下,上面的字符串拼接的例子就是用到了這種優(yōu)化方式),鎖粗化,我們下次再繼續(xù)分享。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/76658.html
摘要:物理計算機并發(fā)問題在介紹內(nèi)存模型之前,先簡單了解下物理計算機中的并發(fā)問題。基于高速緩存的存儲交互引入一個新的問題緩存一致性。寫入作用于主內(nèi)存變量,把操作從工作內(nèi)存中得到的變量值放入主內(nèi)存的變量中。 物理計算機并發(fā)問題 在介紹Java內(nèi)存模型之前,先簡單了解下物理計算機中的并發(fā)問題。由于處理器的與存儲設(shè)置的運算速度有幾個數(shù)量級的差距,所以現(xiàn)代計算機加入一層讀寫速度盡可能接近處理器的高速緩...
摘要:第一個字被稱為。經(jīng)量級鎖的加鎖過程當一個對象被鎖定時,被復制到當前嘗試獲取鎖的線程的線程棧的鎖記錄空間被復制的官方稱為。根據(jù)鎖對象目前是否處于被鎖定狀態(tài),撤銷偏向后恢復到未鎖定或經(jīng)量級鎖定狀態(tài)。 Synchronized關(guān)鍵字 synchronized的鎖機制的主要優(yōu)勢是Java語言內(nèi)置的鎖機制,因此,JVM可以自由的優(yōu)化而不影響已存在的代碼。 任何對象都擁有對象頭這一數(shù)據(jù)結(jié)構(gòu)來支持鎖...
摘要:內(nèi)存模型是圍繞著在并發(fā)過程中如何處理原子性可見性和有序性這個特征來建立的,我們來看下哪些操作實現(xiàn)了這個特性。可見性可見性是指當一個線程修改了共享變量的值,其他線程能夠立即得知這個修改。 Java內(nèi)存模型是圍繞著在并發(fā)過程中如何處理原子性、可見性和有序性這3個特征來建立的,我們來看下哪些操作實現(xiàn)了這3個特性。 原子性(atomicity): 由Java內(nèi)存模型來直接保證原子性變量操作包括...
摘要:這兩種策略的區(qū)別就在于,公平策略會讓等待時間長的線程優(yōu)先執(zhí)行,非公平策略則是等待時間長的線程不一定會執(zhí)行,存在一個搶占資源的問題。 之前有一篇文章我們簡單的談到了Java中同步的問題,但是可能在平常的開發(fā)中,有些理論甚至是某些方式是用不到的,但是從程序的角度看,這些理論思想我們可以運用到我們的開發(fā)中,比如是不是應該一談到同步問題,就應該想到用synchronized?,什么時候應該用R...
摘要:并發(fā)需要解決的問題功能性問題線程同步面臨兩個問題,想象下有兩個線程在協(xié)作工作完成某項任務(wù)。鎖可用于規(guī)定一個臨界區(qū),同一時間臨界區(qū)內(nèi)僅能由一個線程訪問。并發(fā)的數(shù)據(jù)結(jié)構(gòu)線程安全的容器,如等。 并發(fā)指在宏觀上的同一時間內(nèi)同時執(zhí)行多個任務(wù)。為了滿足這一需求,現(xiàn)代的操作系統(tǒng)都抽象出 線程 的概念,供上層應用使用。 這篇博文不打算詳細展開分析,而是對java并發(fā)中的概念和工具做一個梳理。沿著并發(fā)模...
閱讀 2861·2021-10-14 09:50
閱讀 1218·2021-10-08 10:21
閱讀 3646·2021-10-08 10:16
閱讀 3063·2021-09-27 14:02
閱讀 3135·2021-09-23 11:21
閱讀 2109·2021-09-07 10:17
閱讀 407·2019-08-30 14:00
閱讀 2105·2019-08-29 17:26