淺談Java多線程的優(yōu)點(diǎn)及代碼示例
盡管面臨很多挑戰(zhàn),多線程有一些優(yōu)點(diǎn)使得它一直被使用。這些優(yōu)點(diǎn)是:
資源利用率更好
程序設(shè)計(jì)在某些情況下更簡單
程序響應(yīng)更快
資源利用率更好
想象一下,一個(gè)應(yīng)用程序需要從本地文件系統(tǒng)中讀取和處理文件的情景。比方說,從磁盤讀取一個(gè)文件需要5秒,處理一個(gè)文件需要2秒。處理兩個(gè)文件則需要:
5秒讀取文件A 2秒處理文件A 5秒讀取文件B 2秒處理文件B --------------------- 總共需要14秒
從磁盤中讀取文件的時(shí)候,大部分的CPU時(shí)間用于等待磁盤去讀取數(shù)據(jù)。在這段時(shí)間里,CPU非常的空閑。它可以做一些別的事情。通過改變操作的順序,就能夠更好的使用CPU資源。看下面的順序:
5秒讀取文件A 5秒讀取文件B + 2秒處理文件A 2秒處理文件B --------------------- 總共需要12秒
CPU等待第一個(gè)文件被讀取完。然后開始讀取第二個(gè)文件。當(dāng)?shù)诙募诒蛔x取的時(shí)候,CPU會(huì)去處理第一個(gè)文件。記住,在等待磁盤讀取文件的時(shí)候,CPU大部分時(shí)間是空閑的。
總的說來,CPU能夠在等待IO的時(shí)候做一些其他的事情。這個(gè)不一定就是磁盤IO。它也可以是網(wǎng)絡(luò)的IO,或者用戶輸入。通常情況下,網(wǎng)絡(luò)和磁盤的IO比CPU和內(nèi)存的IO慢的多。
程序設(shè)計(jì)更簡單
在單線程應(yīng)用程序中,如果你想編寫程序手動(dòng)處理上面所提到的讀取和處理的順序,你必須記錄每個(gè)文件讀取和處理的狀態(tài)。相反,你可以啟動(dòng)兩個(gè)線程,每個(gè)線程處理一個(gè)文件的讀取和操作。線程會(huì)在等待磁盤讀取文件的過程中被阻塞。在等待的時(shí)候,其他的線程能夠使用CPU去處理已經(jīng)讀取完的文件。其結(jié)果就是,磁盤總是在繁忙地讀取不同的文件到內(nèi)存中。這會(huì)帶來磁盤和CPU利用率的提升。而且每個(gè)線程只需要記錄一個(gè)文件,因此這種方式也很容易編程實(shí)現(xiàn)。
程序響應(yīng)更快
將一個(gè)單線程應(yīng)用程序變成多線程應(yīng)用程序的另一個(gè)常見的目的是實(shí)現(xiàn)一個(gè)響應(yīng)更快的應(yīng)用程序。設(shè)想一個(gè)服務(wù)器應(yīng)用,它在某一個(gè)端口監(jiān)聽進(jìn)來的請求。當(dāng)一個(gè)請求到來時(shí),它去處理這個(gè)請求,然后再返回去監(jiān)聽。
服務(wù)器的流程如下所述:
while(server is active){ listen for request process request }
如果一個(gè)請求需要占用大量的時(shí)間來處理,在這段時(shí)間內(nèi)新的客戶端就無法發(fā)送請求給服務(wù)端。只有服務(wù)器在監(jiān)聽的時(shí)候,請求才能被接收。另一種設(shè)計(jì)是,監(jiān)聽線程把請求傳遞給工作者線程(worker thread),然后立刻返回去監(jiān)聽。而工作者線程則能夠處理這個(gè)請求并發(fā)送一個(gè)回復(fù)給客戶端。這種設(shè)計(jì)如下所述:
while(server is active){ listen for request hand request to worker thread }
這種方式,服務(wù)端線程迅速地返回去監(jiān)聽。因此,更多的客戶端能夠發(fā)送請求給服務(wù)端。這個(gè)服務(wù)也變得響應(yīng)更快。
桌面應(yīng)用也是同樣如此。如果你點(diǎn)擊一個(gè)按鈕開始運(yùn)行一個(gè)耗時(shí)的任務(wù),這個(gè)線程既要執(zhí)行任務(wù)又要更新窗口和按鈕,那么在任務(wù)執(zhí)行的過程中,這個(gè)應(yīng)用程序看起來好像沒有反應(yīng)一樣。相反,任務(wù)可以傳遞給工作者線程(word thread)。當(dāng)工作者線程在繁忙地處理任務(wù)的時(shí)候,窗口線程可以自由地響應(yīng)其他用戶的請求。當(dāng)工作者線程完成任務(wù)的時(shí)候,它發(fā)送信號給窗口線程。窗口線程便可以更新應(yīng)用程序窗口,并顯示任務(wù)的結(jié)果。對用戶而言,這種具有工作者線程設(shè)計(jì)的程序顯得響應(yīng)速度更快。
下面是借鑒別人的一個(gè)Java多線程的例子,感謝。
問題:三個(gè)售票窗口同時(shí)出售20張票;
程序分析:
1.票數(shù)要使用同一個(gè)靜態(tài)值
2.為保證不會(huì)出現(xiàn)賣出同一個(gè)票數(shù),要java多線程同步鎖。
設(shè)計(jì)思路:1.創(chuàng)建一個(gè)站臺類Station,繼承Thread,重寫run方法,在run方法里面執(zhí)行售票操作!售票要使用同步鎖:即有一個(gè)站臺賣這張票時(shí),其他站臺要等這張票賣完!
創(chuàng)建主方法調(diào)用類
(一)創(chuàng)建一個(gè)站臺類,繼承Thread
(原文有一點(diǎn)點(diǎn)小錯(cuò)誤,小編已經(jīng)更正,大家請放心測試使用)
package com.xykj.threadStation; public class Station extends Thread { // 通過構(gòu)造方法給線程名字賦值 public Station(String name) { super(name); // 給線程名字賦值 } // 為了保持票數(shù)的一致,票數(shù)要靜態(tài) static int tick = 20; // 創(chuàng)建一個(gè)靜態(tài)鑰匙 static Object ob = "aa"; //值是任意的 // 重寫run方法,實(shí)現(xiàn)買票操作 @Override public void run() { while (tick > 0) { synchronized (ob) { // 這個(gè)很重要,必須使用一個(gè)鎖, // 進(jìn)去的人會(huì)把鑰匙拿在手上,出來后才把鑰匙拿讓出來 if (tick > 0) { System.out.println(getName() + "賣出了第" + tick + "張票"); tick--; } else { System.out.println("票賣完了"); } } try { sleep(1000); //休息一秒 } catch (InterruptedException e) { e.printStackTrace(); } } } }
(二)創(chuàng)建主方法調(diào)用類
package com.xykj.threadStation; public class MainClass { /** * java多線程同步鎖的使用 * 示例:三個(gè)售票窗口同時(shí)出售10張票 * */ public static void main(String[] args) { //實(shí)例化站臺對象,并為每一個(gè)站臺取名字 Station station1=new Station("窗口1"); Station station2=new Station("窗口2"); Station station3=new Station("窗口3"); // 讓每一個(gè)站臺對象各自開始工作 station1.start(); station2.start(); station3.start(); } }
運(yùn)行結(jié)果:
窗口1賣出了第20張票 窗口2賣出了第19張票 窗口3賣出了第18張票 窗口1賣出了第17張票 窗口2賣出了第16張票 窗口3賣出了第15張票 窗口3賣出了第14張票 窗口2賣出了第13張票 窗口1賣出了第12張票 窗口3賣出了第11張票 窗口2賣出了第10張票 窗口1賣出了第9張票 窗口1賣出了第8張票 窗口2賣出了第7張票 窗口3賣出了第6張票 窗口1賣出了第5張票 窗口3賣出了第4張票 窗口2賣出了第3張票 窗口3賣出了第2張票 窗口2賣出了第1張票 票賣完了
總結(jié)
以上就是本文關(guān)于淺談Java多線程的優(yōu)點(diǎn)及代碼示例的全部內(nèi)容,希望對大家有所幫助。感興趣的朋友可以繼續(xù)參閱:Java多線程之線程通信生產(chǎn)者消費(fèi)者模式及等待喚醒機(jī)制代碼詳解、Java編程之多線程死鎖與線程間通信簡單實(shí)現(xiàn)代碼、Java多線程編程小實(shí)例模擬停車場系統(tǒng)等,有什么問題可以隨時(shí)留言,小編會(huì)及時(shí)回復(fù)大家的。感謝朋友們對本站的支持!
相關(guān)文章
Spring中的@PostConstruct注解使用方法解析
這篇文章主要介紹了Spring中的@PostConstruct注解使用方法解析,@PostConstruct注解是用來處理在@Autowired注入屬性后init()方法之前,對一些零散的屬性進(jìn)行賦值的注解,需要的朋友可以參考下2023-11-11Netty中的DelimiterBasedFrameDecoder使用方法詳解
這篇文章主要介紹了Netty中的DelimiterBasedFrameDecoder使用方法詳解,DelimiterBasedFrameDecoder與LineBasedFrameDecoder類似,只不過更加通用,允許我們指定任意特殊字符作為分隔符,我們還可以同時(shí)指定多個(gè)分隔符,需要的朋友可以參考下2023-12-12java設(shè)計(jì)模式(實(shí)戰(zhàn))-責(zé)任鏈模式
這篇文章主要介紹了java設(shè)計(jì)模式(實(shí)戰(zhàn))-責(zé)任鏈模式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01SpringBoot使用@Validated處理校驗(yàn)的方法步驟
@Validated?注解的主要目的是啟用和利用?Spring?的驗(yàn)證框架,它可以用于類上也可以用于方法參數(shù)上,本文給大家介紹了SpringBoot使用@Validated優(yōu)雅的處理校驗(yàn)的方法步驟,通過代碼示例介紹的非常詳細(xì),需要的朋友可以參考下2024-08-08java.lang.AbstractMethodError: org.apache.xerces.dom.Documen
這篇文章主要介紹了java.lang.AbstractMethodError: org.apache.xerces.dom.DocumentImpl.setXmlVersion問題解決方法,導(dǎo)致本文問題的原因是缺少一個(gè)xerces.jar jar包,需要的朋友可以參考下2015-03-03詳解SpringCloud eureka服務(wù)狀態(tài)監(jiān)聽
這篇文章主要介紹了詳解SpringCloud eureka服務(wù)狀態(tài)監(jiān)聽,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-07-07drools規(guī)則動(dòng)態(tài)化實(shí)踐解析
這篇文章主要為大家介紹了drools規(guī)則動(dòng)態(tài)化實(shí)踐解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02Java 超詳細(xì)講解IO操作字節(jié)流與字符流
本章具體介紹了字節(jié)流、字符流的基本使用方法,圖解穿插代碼實(shí)現(xiàn)。 JAVA從基礎(chǔ)開始講,后續(xù)會(huì)講到JAVA高級,中間會(huì)穿插面試題和項(xiàng)目實(shí)戰(zhàn),希望能給大家?guī)韼椭?/div> 2022-03-03詳解JavaEE使用過濾器實(shí)現(xiàn)登錄(用戶自動(dòng)登錄 安全登錄 取消自動(dòng)登錄黑用戶禁止登錄)
主要介紹用戶的自動(dòng)登錄和取消自動(dòng)登錄,以及實(shí)現(xiàn)一天自動(dòng)登錄或者n天實(shí)現(xiàn)自動(dòng)登錄,當(dāng)用戶ip被加入到黑名單之后,直接利用過濾器返回一個(gè)警告頁面。接下來通過本文給大家介紹JavaEE使用過濾器實(shí)現(xiàn)登錄的相關(guān)知識,感興趣的朋友一起學(xué)習(xí)吧2016-05-05最新評論