Java編程語言需要注意線程安全和同步
Java編程語言對(duì)于使用者來說是一個(gè)相當(dāng)簡(jiǎn)單的編程語言。但是在使用的時(shí)候還是需要我們不斷注意相關(guān)問題,下面我們就來熟悉下Java編程語言的線程安全問題和信息同步。
就其自身來說,并發(fā)編程是一種技術(shù),提供了操作的同時(shí)執(zhí)行,不論是在單一系統(tǒng)上還是分布在大量系統(tǒng)上。這類操作實(shí)際是一些指令順序,例如單獨(dú)某個(gè)***任務(wù)的子任務(wù),這類操作能夠并行執(zhí)行,或者是作為線程,或者是作為進(jìn)程。線程和進(jìn)程之間的本質(zhì)區(qū)別在于:進(jìn)程通常是獨(dú)立的(例如獨(dú)立的地址空間),所以只能通過系統(tǒng)提供的進(jìn)程間通信機(jī)制進(jìn)行交互,而線程通常共享單一進(jìn)程的狀態(tài)信息,能夠直接共享系統(tǒng)資源和內(nèi)存中的對(duì)象。
可以使用下面兩種方法之一,通過多個(gè)進(jìn)程來實(shí)現(xiàn)并發(fā)。***種方法是在同一個(gè)處理器上運(yùn)行進(jìn)程,由操作系統(tǒng)處理進(jìn)程之間的上下文環(huán)境切換。(可以理解,這種切換要比同一進(jìn)程內(nèi)多線程之間的上下文環(huán)境切換更慢。)第二種方法是構(gòu)建大規(guī)模的并行和復(fù)雜的分布式系統(tǒng),在不同的物理處理器上運(yùn)行多個(gè)進(jìn)程。
從內(nèi)建支持的角度來說,Java編程語言通過線程提供并發(fā)編程;每個(gè) JVM 都能支持許多線程同時(shí)執(zhí)行??梢杂靡韵聝煞N方法之一在Java編程語言中創(chuàng)建線程:
繼承 java.lang.Thread 類。在這種情況下,已經(jīng)重寫的子類的 run() 方法必須包含實(shí)現(xiàn)線程運(yùn)行時(shí)行為的代碼。要執(zhí)行這個(gè)代碼,需要實(shí)例化子類對(duì)象,然后調(diào)用對(duì)象的 start() 方法,這樣就可以在內(nèi)部執(zhí)行 run() 方法了。#t#
創(chuàng)建 Runnable 接口的定制實(shí)現(xiàn)。這個(gè)接口只包含一個(gè) run() 方法,在這個(gè)方法中,要放置應(yīng)用程序代碼。要執(zhí)行這個(gè)代碼,需要實(shí)例化實(shí)現(xiàn)類的對(duì)象,然后在創(chuàng)建新 Thread 時(shí),把對(duì)象作為構(gòu)造函數(shù)的參數(shù)傳入。然后調(diào)用新創(chuàng)建的線程對(duì)象的 start() 方法,開始執(zhí)行控制的新線程。
線程安全性和同步
如果Java編程語言對(duì)象中的某個(gè)方法能夠安全地運(yùn)行在多線程環(huán)境中,那么就稱該方法是 線程安全的。要獲得這種安全性,必須有一種機(jī)制,通過該機(jī)制,運(yùn)行同一方法的多個(gè)線程就能夠同步其操作,這樣,在訪問相同的對(duì)象或代碼行時(shí),就會(huì)只允許一個(gè)線程被處理。這種同步要求線程使用叫作 信號(hào) 的對(duì)象彼此進(jìn)行溝通。
有一種類型的信號(hào)叫作 互斥信號(hào) 或 互斥體。顧名思義,這個(gè)信號(hào)對(duì)象的擁有權(quán)是互斥的,也就是說,在任意指定時(shí)間,只有一個(gè)線程能夠擁有互斥體。其他想獲得所有權(quán)的線程會(huì)被阻塞,它們必須等待,直到擁有互斥體的線程釋放互斥體。如果多個(gè)線程按順序排隊(duì)等候同一互斥體,那么在當(dāng)前擁有者釋放它的時(shí)候,只有一個(gè)等候線程能夠得到它;其他線程將繼續(xù)阻塞。
在 1970 年代初,C.A.R. Hoare 和其他人共同開發(fā)了一個(gè)叫作 監(jiān)視器 的概念。一個(gè) 監(jiān)視器 就是一個(gè)代碼主體,它的訪問受到互斥體的保護(hù)。任何想執(zhí)行這個(gè)代碼的線程,都必須在代碼塊頂部得到關(guān)聯(lián)的互斥體,然后在底部再釋放它。因?yàn)樵谥付〞r(shí)間只有一個(gè)線程能夠擁有互斥體,所以這就有效地保證了只有擁有它的線程才能執(zhí)行監(jiān)視器的代碼塊。(受保護(hù)的代碼不需要相鄰 —— 例如,Java編程語言中的每個(gè)對(duì)象都有一個(gè)與之關(guān)聯(lián)的監(jiān)視器。)
任何想在 Java編程語言中進(jìn)行線程編程的開發(fā)人員,都會(huì)立即把上面的內(nèi)容當(dāng)成 synchronized 關(guān)鍵字所帶來的效果。可以確保包含在 synchronized 塊中的 Java 代碼在指定時(shí)間只被一個(gè)線程執(zhí)行。在內(nèi)部,可以由運(yùn)行時(shí)將 synchronized 關(guān)鍵字轉(zhuǎn)換成某一種情況:所有的競(jìng)爭(zhēng)線程都試圖獲得與它們(指線程)正在操作的對(duì)象實(shí)例關(guān)聯(lián)的那個(gè)(惟一的一個(gè))互斥體。成功得到互斥體的線程將運(yùn)行代碼,然后在退出 synchronized 塊時(shí)釋放互斥體。
等候和通知
wait/notify 構(gòu)造在 Java編程語言的線程間通信機(jī)制中也扮演了重要的角色?;镜南敕ㄊ牵阂粋€(gè)線程需要的某個(gè)條件可以由另外一個(gè)線程促成。這樣,條件的 wait 就可以得到滿足。一旦條件為真,那么引發(fā)條件的線程就會(huì) notify 等候線程蘇醒,并從中止的地方繼續(xù)進(jìn)行。
wait/notify 機(jī)制要比 synchronized 機(jī)制更難理解和判斷。要想判斷出使用 wait/notify 的方法的行為邏輯,就要求判斷出使用它的所有方法的邏輯。一次判斷一個(gè)方法,把該方法和其他方法隔離開,是對(duì)整體系統(tǒng)行為得出錯(cuò)誤結(jié)論的可靠方式。顯然,這樣做的復(fù)雜性會(huì)隨著要判斷的方法的數(shù)量增長(zhǎng)而迅速提高。