1. 程式人生 > >Java併發程式設計---必須要懂得概念和思想

Java併發程式設計---必須要懂得概念和思想

其實併發程式設計並沒有我們想的那麼難,很多的時候我們在思考的時候是因為沒有get到併發程式設計的點,很多時候我們都是稀裡糊塗的去學習併發程式設計的,根本很難弄明白併發程式設計要注意什麼,在這個期間我覺得自己走了很多的彎路,現在分享一下我對於併發程式設計的一些理解: (1)為什麼需要採用併發程式設計,併發程式設計需要解決的問題的本意是什麼? 我自己的理解是併發程式設計的目的是為了提高系統的效能,當流量足夠大的時候我們的系統仍然可以較為穩定的執行。 (2)知道併發程式設計要解決的問題之後,我覺得還要提出一個疑問就是,併發程式設計跟我們平常程式設計有什麼區別? 一開始我覺得併發程式設計是屬於很高階的東西,好像很難懂和理解,然後就選擇對他避而不見,以為這個是個難挑的刺,但是最後無奈,靜下心來學習的時候才發現我以前的想法錯了,其實併發程式設計跟我們普通的程式設計是一樣的,只不過我們需要對我們的程式結果要更加的清楚而已。 (3)那麼併發程式設計跟平常的程式設計的不同之處在哪? 我曾經為了瞭解掌握併發程式設計,我也很認真的去買書看,到處看別人的博文,自以為對併發程式設計很瞭解,但事實並非如此,我根本就沒有理解,我只是死記硬背了書上的內容而已,遇到問題總是一知半解。 經過了很久,我才發現處理併發程式設計的問題,有幾個共性,或者說是關鍵的地方,那就是怎麼樣才能確保併發的安全性,發現無非是這三個方面: (1)原子性 (2)可見性 (3)有序性
帶著問題去思考問題是最好的,進行併發程式設計的時候我們清楚的記住這3個地方就可以了 (1)什麼是原子性?程式設計的時候怎麼確保原子性? 原子性:即一個操作或者多個操作,要麼順利的執行(這裡的順利指的是這個過程不能被打斷),要麼不執行。 或許很多人都聽過了這個定義,就好比道理我都懂,但是我就是想不明白啊!其實我們更多的是不明白在Java中什麼樣的操作是原子性的,知道了這個,一切就好辦了。 我們必須要記住的是:在Java中,對基本資料型別變數的讀取和賦值操作是原子性操作。 這句話很短也很簡單,但是想要理解起來卻不是那麼容易的,哈哈 (1)什麼是基本型別? (2)讀取和賦值 第一個很簡答,不知道的話就要認真的學習下基礎,關鍵是第二個,我覺得這個可以簡單的理解為一步操作。 x = 10; y = x; x++; x = x+1; 上面這幾個都是我們經常會用到的,但哪些是原子操作的呢? 其實上面只有 x = 10;這個是屬於院子操作的,其他的都不是,y = x;這個是賦值,但是是分了兩布的,首先要從主存中讀取x的值,然後再把值賦值給y,下面的兩個也是這個道理。所以我們在寫併發程式的時候必須要清楚的明白我們每一行程式碼的結果是不是保證了原子性,那麼其他的三個操作我們怎麼保證原子性呢?可以使用原子類,也可以使用同步,或者加鎖的方式。知道了這些我們是不是要補補原子類,同步,鎖的知識? (2)可見性---怎麼保證可見性呢?
首先一樣我們要明白可見性的含義,其實很簡單,就是我們每一個操作都要需要被其他的執行緒所知道,跟我們今天共享的概念有點一下。 Java中怎麼保證可見性呢?這個時候就需要有一個清晰的認識,有一個關鍵字volatile,對於這個的理解就很重要了,在多個執行緒訪問變數,改變變數的值的時候,我們就要思考一下,我們的執行緒執行的每一步操作,得到的結果對於其他的執行緒是不是可見的。 另外,通過synchronized和Lock也能夠保證可見性,synchronized和Lock能保證同一時刻只有一個執行緒獲取鎖然後執行同步程式碼,並且在釋放鎖之前會將對變數的修改重新整理到主存當中。因此可以保證可見性。 (3)有序性
這裡說的有序性是對於程式執行的步驟來說的,以前我以為有序性,就是程式的程式碼的先後順序,其實真的是對於JMM的理解不夠深入,或者根本就沒有理解好,在Java的記憶體模型中是允許編譯器和處理器對於指令進行重排序的,這個的目的是為了優化程式的執行效率,當然了在單執行緒中這個是不會影響到的,關鍵是多執行緒的時候就出現問題了。、 那麼在Java中怎麼可以確保有序性呢?不讓編譯器和處理器對指令進行重排序呢? volatile,同步,還有鎖這個時候就需要出現在我們的腦海了。還有就是Java本身是有一些動作是可以確保有序性的,那就是我們常說的happen-before原則。 最後,最重要的是:要確保併發程式的安全性,上面的三個條件缺一不可。而只保證一個或者兩個,我們無法確保此時結果的正確性。