李乃健,田紀宏,胥國偉,齊英杰
(濟寧醫(yī)學院醫(yī)學信息工程學院,日照 276825)
計算機真正完成一項具體任務,必須要借助操作系統(tǒng)中的進程來完成,進程是進程實體的運行過程,是系統(tǒng)進行資源分配和調(diào)度的一個獨立單位,但從計算機的效率考察,為了減少系統(tǒng)對于并發(fā)所帶來的時/空開銷,將擁有資源所有權的仍稱為進程,而調(diào)度的單位稱為線程,或輕量級進程,故線程是進程內(nèi)一個相對獨立的執(zhí)行流或控制流,是處理機分配的實體。它本身不擁有系統(tǒng)資源,但與同屬一個進程的其他線程共享進程所擁有的全部資源,進程和多線程的關系如圖1所示。
圖1 進程與線程的關系圖
在操作系統(tǒng)課程中,進程同步是對多個相關進程在執(zhí)行次序上進行協(xié)調(diào),使并發(fā)執(zhí)行的諸進程之間能夠按照一定的規(guī)則(或時序)共享系統(tǒng)資源,并能很好地相互合作,從而使程序的執(zhí)行具有可再現(xiàn)性。從該定義中不難得出:進程同步解決的是同步的諸進程之間執(zhí)行次序的問題,其目的是協(xié)調(diào)多個并發(fā)進程的執(zhí)行,使它們高效地共享系統(tǒng)中的資源并更好地相互合作,從而保證程序的執(zhí)行具有可再現(xiàn)性,使系統(tǒng)資源利用最大化,進而保障系統(tǒng)的穩(wěn)定性與可靠性。
Java語言作為一種面向?qū)ο笄遗c平臺無關的多線程動態(tài)語言,具有支持多線程、解釋運行效率高、動態(tài)性、語法簡單等優(yōu)點,其中最重要的是支持多線程編程。所有Java類都有一個共同的父類:Object類。Ob?ject類有涉及線程同步的notify()、notifyAll()、wait()、wait(long timeOut)等函數(shù),這些函數(shù)可以很好地喚醒或阻塞在當前對象監(jiān)視器上等待線程。Java中線程有4個狀態(tài),創(chuàng)建狀態(tài)、可運行狀態(tài)、運行狀態(tài)、撤消狀態(tài),如圖2所示。
圖2 Java中線程的狀態(tài)轉(zhuǎn)換
在單個程序中同時運行多個線程完成不同的工作,稱為多線程。進程可以看作程序運行時的一個實例,而線程則可看作單獨地占有CPU執(zhí)行的代碼,基于這種思想,使用支持多線程的Java語言編程比其他語言更為簡單與高效。Java中實現(xiàn)多線程編程有兩種主要方法:一種是繼承Thread類,通過定義java.lang包中的Thread類的子類并在子類中重寫run()方法。由于Java不能多重繼承,此方法簡單但不靈活;另一種是實現(xiàn)Runnable接口,該接口只有一個run()方法,要實現(xiàn)此接口就必須定義run()方法的具體內(nèi)容,方法體內(nèi)可定義用戶要做的操作,然后以這個實現(xiàn)了Runnable接口的類為參數(shù)創(chuàng)建Thread類的對象,也就是用Run?nable接口的目標對象初始化Thread類的對象,這樣就可把用戶實現(xiàn)的run()方法繼承過來。
首先,使用多線程技術后,可以在同一時間內(nèi)運行更多不同種類的任務,在開發(fā)難度和性能上都比單線程更好。其次,由于Java語言實現(xiàn)了多線程技術,所以比C、C++等語言實現(xiàn)的算法更為健壯,穩(wěn)定性更好。再者,Java語言是首個在語言級別提供對多線程程序設計支持的編程語言,借助Java語言的多線程機制,開發(fā)多線程應用程序的過程得到大大簡化。而且Java語言引入了并發(fā)機制來避免可能出現(xiàn)的數(shù)據(jù)訪問沖突問題。
(1)問題分析與設計
生產(chǎn)者-消費者問題是一個經(jīng)典的進程同步問題,問題描述為:生產(chǎn)者進程與消費者進程能要想并發(fā)執(zhí)行,需在兩者之間設置一個具有n個緩沖區(qū)(多緩沖區(qū))的緩沖池,生產(chǎn)者進程將其所生產(chǎn)的產(chǎn)品放入一個緩沖區(qū)中;消費者進程可從一個緩沖區(qū)中取走產(chǎn)品消費。不允許消費者進程到一個空緩沖區(qū)去取產(chǎn)品,也不允許生產(chǎn)者進程向一個已裝滿產(chǎn)品且尚未被取走的滿緩沖區(qū)中投放產(chǎn)品。
憑借Java語言多線程編程技術的優(yōu)勢,用線程模擬生產(chǎn)者與消費者,采用已經(jīng)在Java語言內(nèi)部實現(xiàn)了同步機制的阻塞隊列(BlockingQueue)模擬生產(chǎn)者與消費者隊列,利用生產(chǎn)者類Producer與消費者類Consum?er通過實現(xiàn)Runnable接口來的方式創(chuàng)建并實例化線程對象producer與consumer(打破了擴充Thread類與單繼承的限制)。此方法不需要人為地考慮線程何時等待與何時喚醒以及如何清空緩沖區(qū)的問題,從而簡化了代碼的編寫,并且減少了系統(tǒng)的開銷,提高了系統(tǒng)的資源利用率與吞吐量,實現(xiàn)系統(tǒng)資源利用最大化,更提高了程序執(zhí)行的并發(fā)度。在代碼中只新建了一個緩沖區(qū)類Buffer,并且采用匿名內(nèi)部類方式創(chuàng)建并實例化生產(chǎn)者、消費者線程對象,重寫了線程的run()方法作為線程的主體來完成對緩沖隊列LinkedBlockingQueue的操作。阻塞隊列提供的開箱即用的get()與set()方法能夠自動地阻塞線程,在主方法中定義了一個大小為3個線程的緩沖區(qū)隊列和生產(chǎn)者阻塞隊列Producer、消費者阻塞隊列Consumer,它們都采用FIFO(先進先出)策略對線程進行調(diào)度。用同一個緩沖區(qū)對象buffer分別實例化了兩個生產(chǎn)者線程與兩個消費者線程,使他們并發(fā)執(zhí)行。通過Random實例化的對象r調(diào)用nextInt(100)方法會產(chǎn)生0~100內(nèi)的隨機數(shù)作為實參傳給get()方法的形參data,完成生產(chǎn)操作;消費者線程通過調(diào)用take()方法從緩沖區(qū)隊列中取出產(chǎn)品data完成消費操作。
(2)部分代碼實現(xiàn)
Java部分代碼如下:
(1)問題分析與設計
讀者-寫者問題也是一個經(jīng)典的進程同步問題。所謂讀者-寫者問題(The Reader-Writer Problem),是指保證一個Writer進程必須與其他進程互斥地訪問共享對象的同步問題。該問題主要描述的是怎樣保證系統(tǒng)中的若干個進程對某數(shù)據(jù)文件或記錄進行正確地讀寫,從而避免出現(xiàn)文件數(shù)據(jù)的丟失修改與讀臟數(shù)據(jù)的問題。為了避免讀者與寫者同時對文件進行讀寫操作而引起的數(shù)據(jù)訪問錯誤,下面主要研究采用寫者優(yōu)先的方法。所謂寫者優(yōu)先是指一個寫者申請一個共享資源時,如果有讀者在讀取該資源,則必須封鎖后續(xù)到來的讀者,以便寫者對共享資源的修改;當有讀者與寫者同時等待資源時,寫者優(yōu)先訪問共享資源。解決該問題的關鍵在于解決寫者與寫者、寫者與第一個讀者的同步問題。Java不僅支持多線程,而且在Java包中還提供了Lock接口,為多個線程之間的同步提供了互斥鎖,lock()可以實現(xiàn)同步訪問。Lock接口的實現(xiàn)提供了更廣泛的鎖定操作的方法,比使用synchronized方法和語句更加靈活,使算法結構更清晰易讀。
代碼中只新建了一個類ReadAndWrite,采用匿名內(nèi)部類的方式創(chuàng)建并實例化兩個對象,并分別多次循環(huán)調(diào)用Read()與Write()方法對共享數(shù)據(jù)進行讀與寫操作。代碼中還使用可重入的互斥鎖ReentrantRead?WriteLock實現(xiàn)讀者與寫者的互斥。在Read()與Write()方法中設置讀鎖與寫鎖,保證寫者優(yōu)先,SX類中的讀與寫方法操作的數(shù)據(jù)是一個0與1000之間整型的隨機數(shù),本算法還采用了try-catch-finally異常處理機制,當發(fā)生異常時程序會自動解鎖以處理異常。
(2)部分代碼實現(xiàn)
Java部分代碼如下:
從運行結果看,該算法成功實現(xiàn)了生產(chǎn)者線程與消費者線程的同步,它簡化了開發(fā),可以獨立地或并發(fā)地編寫消費者和生產(chǎn)者;生產(chǎn)者和消費者可以以不同的速度執(zhí)行;分離的消費者和生產(chǎn)者在功能上能寫出更簡潔、可讀、易維護的代碼,故應用多線程技術,大大提高了系統(tǒng)效率。
從運行結果來看,本算法很好地實現(xiàn)了“讀者-寫者”問題中寫者優(yōu)先的算法,即當有多個寫者等待時后續(xù)的讀者全部阻塞,直到所有寫者全部寫完后讀者才能讀取,多個寫者之間并發(fā)執(zhí)行,而且讀者讀取的都是最后一個寫者修改后的數(shù)據(jù)。以Java多線程技術與提供的同步機制,有效避免了數(shù)據(jù)訪問沖突與數(shù)據(jù)操作錯誤,即讀者讀取的數(shù)據(jù)為最后一個寫者操作后的數(shù)據(jù)。
通過使用Java語言的多線程技術,仿真實現(xiàn)了生產(chǎn)者-消費者、讀者-寫者問題的進程同步算法。實現(xiàn)了生產(chǎn)者與消費者互斥地使用緩沖區(qū),尤其是Runna?ble接口實現(xiàn)了多個線程共同完成一個任務的功能;實現(xiàn)了寫者優(yōu)先的功能,諸讀者與諸寫者之間各自并發(fā)運行,而且保證讀者讀取數(shù)據(jù)為最后一個寫者操作后的數(shù)據(jù),從而避免了很多算法中都未曾處理的潛在的數(shù)據(jù)沖突和數(shù)據(jù)訪問錯誤等問題。這些仿真實現(xiàn),優(yōu)化了算法的內(nèi)部結構,補充算法的部分功能,增強算法的可讀性和實用性,提高系統(tǒng)資源的利用率,充分發(fā)揮系統(tǒng)的性能。
參考文獻:
[1]孔德鳳,應時.基于Java線程機制研究生產(chǎn)者-消費者問題.信息與電腦[J],2017(2).
[2]湯小丹等.計算機操作系統(tǒng)[M].西安:西安電子科技大學出版社,2014.
[3]高洪巖.Java多線程編程核心技術[M].北京:機械工業(yè)出版社,2015.
[4]吳仁群.Java基礎教程(第二版)[M].北京:清華大學出版社,2012.
[5]史廣.Java多線程并發(fā)機制的應用探討[J].貴州師范大學國際教育學院學報,2016.