java併發

Java多線程編程,是併發編程的一種(另一種重要的併發編程是多進程編程)。我們寫java程序一般是運行在同一個進程中的,所以可以簡單的認爲:併發 編程 = 多線程編程,讓寫操作系統的人去關注多進程編程吧。多線程編程是一個重要的軟件基礎,不管你的代碼是不是多線程,java程序運行在jvm中一定是多線程 運行的:運行你的main方法的線程,以及一些後臺守護線程,如垃圾收集等。雖然在我們平時的程序中很少直接用到多線程,但是一些重要的框架都要用到多線 程的。比如java應用服務器,Servlet,以及其他的任何一個知名的框架。所以,學好多線程編程,對我們理解框架的實現,提高軟件設計水平,以及寫 出更好的可擴展,可伸縮的軟件具有非常重要的作用。

多線程編程是非常複雜的,主要體現在多線程程序非常容易引入bug,這些bug通常難以調試重現,同時是在程序在高併發高負載的時候偶然出現,而且很難重 復出現,所以調試這些bug是一個很困難的事。既然難麼困難,爲什麼要用多線程呢,單線程不也挺好的嗎?明知山有虎,偏向虎山行?那是人家體現自己的勇氣 用的。採用多線程又有什麼好處呢?總的來說,有以下兩點好處:

1. 速度。首先,現在多核CPU已經爛大街了,你的CPU如果還是單核的你都不好意思和人打招呼。採用多線程可以“充分”利用CPU的處理能力。其實,更重要的一點是,併發通常可以提高運行在單核處理器上的程序的性能。這聽起來似乎違背直覺。導致這個現象產生的原因是 阻塞 。程序要很多的地方會產生阻塞,比如IO操作。此時單線程程序就會一直阻塞,直到線程的資源都就緒。這會大大浪費CPU的處理能力。

2. 改進代碼設計。面向對象就是要讓程序以一種和自然世界相似的思維來設計程序,多線程編程可以以更加接近真實世界的方式來思維,使程序更加簡單易懂。比如仿真,如果不採用多線程,基本上沒法實現。

併發編程是軟件設計的基礎,是可以離開任何程序語言來獨立研究的。但是筆者認爲那是計算機科學家乾的事,比如Doug Lea,Brian Goetz 等,筆者竊認爲自己還沒有達到這種境界。所以筆者只有結合java來說說多線程編程。Java在語言層面就支持多線程,而且有大量的類庫來用於多線程編 程,所以可以輕鬆用java寫出多線程程序,做到以前只有專家能做到的事(寫出來的程序的質量當然有天淵之別,呵呵)。我覺得,java從以下三方面來支 持多線程編程:

1. 多線程的實現機制。Java採用命令模式來實現多線程,即 執行器+ 任務 的方式。

2. 多線程的底層協調機制。從最基本的同步到各種用途的鎖,讓程序員精確控制線程之間的協作(合作和競爭)。

3. 多線程的高層協調工具類。各種原子變量,非阻塞隊列,同步容器,同步器,讓線程和線程之間的協作更簡單方便。

下面分別從這三方面來闡述java的多線程編程。

一. 多線程的實現機制

Thread,Executor,ExecutorService,Runnable,Callable,Future……..當筆者看到這麼多的類時, 感到一陣頭皮發麻。相信很多同仁也有一樣的感覺。其實不止多線程,IO系統啊,Socket編程啊,都會讓人有這種感覺,許多初學者望而怯步。但是我們只 要以設計模式的思想去思考這些系統的實現,然後用某個設計模式慢慢梳理看似亂成一鍋的類和接口時,就會發覺這些類庫設計得很精妙和優雅。甚至對SUN的那 幫人產生那什麼XXX江水XXX連綿不絕的敬仰(不得不說,在技術上,過度的個人崇拜是不好的)。比如,用裝飾者模式來分析IO系統,把那一大堆分成資源 流和過濾器流之後,整個IO系統就簡單明瞭的多了。把java的網絡編程分成底層的Socket編程和高層的URL編程之後,又有豁然開朗的感覺。那麼多 線程的這一大堆類和接口又有什麼玄機呢?答。案。就。是。。。。。。命令模式!首先分清楚執行器和任務這兩個概念。執行器就是執行任務的器(?)。任務就是具體要實現的功能,依附在執行器上得以執行(以上兩概念純屬瞎掰,不必較真,大概理解就行)。Thread,Executor,ExecutorService等屬於執行器,而Runnable,Callable,FutureTask屬於任務。程序運行時需要創建一個執行器,若干任務,任務依附在執行器上,由執行器啓動執行並控制任務。至於ThreadFactory和UnCaughtExceptionHandler是 指定執行的方式以及拋出異常時的處理,可以分別學習。值得一提的是一個重要的類是Future,它可以實現線程有返回值。返回的結果就放在Future裏 面,隨時可以用get()來檢查執行結果。還可以用cancel()來對線程執行interrupt(),功能十分強大。

二. 多線程的底層協調機制

編寫多線程程序最重要的一個問題是對共享資源(或者叫共享,可變的狀態)的訪問,訪問共享資源也是線程間相互通信的簡單方法。而多個線程一起訪問共享資源,就要注意線程安全。編寫線程安全的代碼,本質上是管理對狀態的訪問,而且通常是共享的,可變的狀態.所謂共享,是指一個變量可以被多個線程訪問;所謂可變,是指變量在生命週期內可以給便。我們討論的線程安全好像是關於代碼的,但真正要做的,是在不可控制的併發訪問中保護數據。

Java提供底層的協調機制,以控制對共享資源的訪問。這裏的協調,包括競爭,合作和通信。

競爭是通過鎖來實現的,在訪問資源之前要先取得鎖,如果鎖正被其他線程佔有,那麼本線程就會阻塞,直到取得鎖。具體的實現方式有兩種:

1. synchronized關鍵字。Synchronized的語義是 想要執行被包圍的代碼塊或方法,必須先取得它聲明的那個對象鎖。如果不能取得,線程到這裏就阻塞,直到取得對象鎖。至於這樣做是否能夠真正保護了共享資源 的訪問,synchronized關鍵字是不管的。需要由程序員自己來保證:確保共享資源是私有的,所有訪問共享資源的地方都加上了 synchronized關鍵字,而且使用的是同一個對象鎖。Synchronized不僅能保證操作的原子性,還可以保證變量的可見性。而 volatile僅能保證變量的可見性。(有關原子性和可見性不知道要放哪裏,這裏先提一下)

2. 使用concurrent.lock。使用concurrent.lock不僅能實現synchronized的全部功能,而且能提供更好的性能和更精確 的語義。比如,synchronized用的是互斥鎖,僅能實現對資源的互斥訪問,爲concurrent.lock不僅有互斥鎖,還有讀寫鎖。把讀鎖和 寫鎖分開,寫鎖相當於互斥鎖,而讀鎖是共享的,可以讓多線程同時讀,以提高性能。

合作:線程之間的合作包括Object.wait/notify /notifyAll,Thread.interrupt, Condition.await/signal/signalAll。可以在資源未準備好時調用Object.wait()使當前線程進入阻塞,而其他線 程在資源準備好時調用notify把所有在Object上阻塞的線程喚醒,進入可執行狀態。Condition.await/signal /signalAll是jdk5提供的更加精細控制線程的類,可以組合不同的Condition來實現複雜的控制。

通信:線程之間的通信通過PipedReader / PipedWriter組合成管道對。不同線程的線程可以在上面讀寫,從而實現通信。程序員可以方便地利用它們來實現“生產者-消費者”模型。

三.多線程的高層協調工具類

用底層的協調機制可以靈活實現各種各樣的需求,但這樣容易出錯,對程序員的要求很高。JDK1.5推出了很多高層的協調工具類,這些類讓我們可以不用再用 synchronized或者Lock來控制併發,只要簡單實用它提供的容器,同步器等,就可以實現併發訪問。主要的類也可以用競爭,合作及通信來劃分。 主要包括:

1. 各種特定用途的容器,方便線程之間的通信。如BlockingQueue,DelayQueue,ConcurrentHashMap,CopyOnWriteArrayList等。

2. 線程安全的基本變量類,在包java.util.concurrent.atomic中提供。

3. 各種用途的同步器synchronizer,如CountDownLatch,CyclicBarrier,Semaphore,Exchange。

4. 計時。TimeUnit類爲指定和控制基於超時的操作提供了多重粒度(包括納秒級)。以代替簡陋的Thread.sleep。

以下將分別介紹:(暫略)

附A:併發編程需要掌握的一些基本概念:

1. 摩爾定律:集成電路芯片上所集成的晶體管數量,越每隔18個月便會翻一番。

2. Amdahl定律:對計算機系統的某個部件採用優化措施後所獲得的計算機性能的提高,依賴於這部分的執行時間在整個運行時間中所佔的比率。

3. 競爭條件:多個任務併發訪問和操作同一數據且執行結果與訪問的特定順序有關,稱爲競爭條件。(多個任務競爭響應某個條件,因訪問順序不同產生衝突或不一致的情況)。比如“檢查再運行”“惰性初始化”。

4. 原子操作:任務在執行過程中不能被打斷的一序列操作

5. 複合操作:任務在執行過程中可以被打斷的一序列操作

6. 不變約束:不變式表達了對狀態的約束,這些狀態是應該符合這個約束的值的組合。不變式可以代表某種業務規則。

7. 先驗條件:針對方法,規定了在條用方法之前必須爲真的條件

8. 後驗條件:針對方法,規定了在條用方法之後必須爲真的條件

9. 原子性:(見原子操作)

10. 可見性:確保線程對變量的寫入對其他線程是可見的。即刷新內存中的變量。

 

發佈了26 篇原創文章 · 獲贊 4 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章