JAVA多線程實(shí)現(xiàn)的四種方式及使用場(chǎng)景詳解
一、繼承Thread類實(shí)現(xiàn)多線程
- 步驟一:定義線程類
創(chuàng)建一個(gè)類繼承自Thread類,并重寫run方法。run方法中的代碼就是線程執(zhí)行的內(nèi)容。
例如:
class MyThread extends Thread { @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println("MyThread: " + i); } } }
- 步驟二:?jiǎn)?dòng)線程
在main方法或者其他合適的地方創(chuàng)建線程對(duì)象,然后調(diào)用start方法來啟動(dòng)線程。
例如:
public class Main { public static void main(String[] args) { MyThread thread = new MyThread(); thread.start(); for (int i = 0; i < 10; i++) { System.out.println("Main Thread: " + i); } } }
注意,不能直接調(diào)用run方法來啟動(dòng)線程。如果直接調(diào)用run方法,就相當(dāng)于在當(dāng)前線程中執(zhí)行run方法中的代碼,而不是開啟一個(gè)新的線程。
二、實(shí)現(xiàn)Runnable接口實(shí)現(xiàn)多線程
- 步驟一:定義任務(wù)類
創(chuàng)建一個(gè)類實(shí)現(xiàn)Runnable接口,實(shí)現(xiàn)run方法。這個(gè)run方法包含了線程要執(zhí)行的任務(wù)。
例如:
java class MyRunnable implements Runnable { @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println("MyRunnable: " + i); } } }
- 步驟二:創(chuàng)建線程并啟動(dòng)
首先創(chuàng)建Runnable對(duì)象,然后將其作為參數(shù)傳遞給Thread對(duì)象的構(gòu)造函數(shù),最后調(diào)用Thread對(duì)象的start方法來啟動(dòng)線程。
例如:
public class Main { public static void main(String[] args) { MyRunnable runnable = new MyRunnable(); Thread thread = new Thread(runnable); thread.start(); for (int i = 0; i < 10; i++) { System.out.println("Main Thread: " + i); } } }
實(shí)現(xiàn)Runnable接口的優(yōu)勢(shì)
這種方式更靈活,因?yàn)橐粋€(gè)類可以實(shí)現(xiàn)多個(gè)接口,避免了單繼承的限制。例如,如果一個(gè)類已經(jīng)繼承了其他類,還想實(shí)現(xiàn)多線程功能,就可以使用實(shí)現(xiàn)Runnable接口的方式。而且Runnable對(duì)象可以被多個(gè)線程共享,方便在多個(gè)線程中執(zhí)行相同的任務(wù)。
三、使用Callable和Future實(shí)現(xiàn)多線程(帶有返回值)
- 步驟一:定義Callable任務(wù)類
創(chuàng)建一個(gè)類實(shí)現(xiàn)Callable接口,實(shí)現(xiàn)call方法。call方法中包含線程要執(zhí)行的任務(wù),并且可以有返回值。
例如:
import java.util.concurrent.Callable; class MyCallable implements Callable<Integer> { @Override public Integer call() { int sum = 0; for (int i = 0; i < 10; i++) { sum += i; } return sum; } }
- 步驟二:提交任務(wù)并獲取結(jié)果
通過ExecutorService來提交Callable任務(wù)。ExecutorService可以通過Executors工廠類來創(chuàng)建。
例如:
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class Main { public static void main(String[] args) throws Exception { ExecutorService executorService = Executors.newSingleThreadExecutor(); MyCallable callable = new MyCallable(); Future<Integer> future = executorService.submit(callable); System.out.println("計(jì)算結(jié)果: " + future.get()); executorService.shutdown(); } }
在這里,submit方法提交Callable任務(wù)并返回一個(gè)Future對(duì)象,通過Future對(duì)象的get方法可以獲取Callable任務(wù)的返回值。shutdown方法用于關(guān)閉ExecutorService。
四、線程池的使用(ExecutorService)
- 步驟一:創(chuàng)建線程池
可以使用Executors工廠類創(chuàng)建不同類型的線程池,如newFixedThreadPool(固定大小線程池)、newCachedThreadPool(可緩存線程池)、newSingleThreadExecutor(單線程線程池)等。
例如,創(chuàng)建一個(gè)固定大小為 5 的線程池:
ExecutorService executorService = Executors.newFixedThreadPool(5);
- 步驟二:提交任務(wù)到線程池
可以使用execute方法提交Runnable任務(wù),或者使用submit方法提交Callable任務(wù)到線程池。
例如,提交一個(gè)Runnable任務(wù):
class MyRunnableInPool implements Runnable { @Override public void run() { System.out.println("線程池中的線程在執(zhí)行任務(wù)"); } } public class Main { public static void main(String[] args) { ExecutorService executorService = Executors.newFixedThreadPool(5); for (int i = 0; i < 10; i++) { executorService.execute(new MyRunnableInPool()); } executorService.shutdown(); } }
- 步驟三:關(guān)閉線程池
當(dāng)任務(wù)執(zhí)行完畢后,需要關(guān)閉線程池??梢允褂胹hutdown方法來正常關(guān)閉線程池,它會(huì)等待所有已提交的任務(wù)執(zhí)行完畢后再關(guān)閉。如果想要立即關(guān)閉線程池,可以使用shutdownNow方法,但這種方式可能會(huì)導(dǎo)致正在執(zhí)行的任務(wù)被中斷。
例如:
executorService.shutdown();
Q:一個(gè)管理系統(tǒng)的java項(xiàng)目,什么時(shí)候會(huì)需要用到多線程呢?
1、數(shù)據(jù)導(dǎo)入與導(dǎo)出功能
背景和需求:
在管理系統(tǒng)中,經(jīng)常需要進(jìn)行數(shù)據(jù)的導(dǎo)入和導(dǎo)出操作。例如,將大量的用戶信息、訂單數(shù)據(jù)等從外部文件(如 CSV、Excel 文件)導(dǎo)入到數(shù)據(jù)庫(kù)中,或者將系統(tǒng)中的數(shù)據(jù)導(dǎo)出為報(bào)表文件。這些操作可能涉及大量的數(shù)據(jù)處理,如果在單線程中執(zhí)行,會(huì)導(dǎo)致界面長(zhǎng)時(shí)間無響應(yīng),用戶體驗(yàn)差。
多線程的應(yīng)用方式:
可以開啟一個(gè)單獨(dú)的線程來執(zhí)行數(shù)據(jù)導(dǎo)入或?qū)С鋈蝿?wù)。這樣,在數(shù)據(jù)處理的同時(shí),用戶界面仍然可以響應(yīng)用戶的其他操作,如查看其他數(shù)據(jù)、進(jìn)行系統(tǒng)設(shè)置等。例如,當(dāng)用戶點(diǎn)擊 “導(dǎo)入數(shù)據(jù)” 按鈕時(shí),系統(tǒng)在后臺(tái)線程中讀取文件、解析數(shù)據(jù)并插入到數(shù)據(jù)庫(kù),而前臺(tái)線程繼續(xù)響應(yīng)用戶的其他交互。
2、數(shù)據(jù)緩存更新與維護(hù)
背景和需求:
為了提高系統(tǒng)性能,管理系統(tǒng)通常會(huì)使用數(shù)據(jù)緩存。緩存的數(shù)據(jù)需要定期更新,以保證數(shù)據(jù)的一致性和時(shí)效性。例如,緩存的商品庫(kù)存信息、用戶權(quán)限信息等需要根據(jù)數(shù)據(jù)庫(kù)中的最新數(shù)據(jù)進(jìn)行更新。
多線程的應(yīng)用方式:
可以使用一個(gè)線程定期(如每隔一段時(shí)間)檢查緩存數(shù)據(jù)是否過期,并在需要時(shí)更新緩存。這個(gè)線程可以在后臺(tái)默默地運(yùn)行,不影響系統(tǒng)的其他主要功能。同時(shí),在數(shù)據(jù)發(fā)生變化(如用戶修改了商品庫(kù)存)時(shí),也可以通過多線程機(jī)制及時(shí)更新緩存,以減少對(duì)系統(tǒng)其他操作的影響。
3、并發(fā)用戶操作處理
背景和需求:
在多用戶使用的管理系統(tǒng)中,會(huì)有多個(gè)用戶同時(shí)進(jìn)行各種操作,如查詢數(shù)據(jù)、修改記錄、提交表單等。如果系統(tǒng)是單線程的,這些操作只能依次進(jìn)行,效率低下。
多線程的應(yīng)用方式:
為每個(gè)用戶請(qǐng)求分配一個(gè)獨(dú)立的線程來處理。例如,在一個(gè)在線商城管理系統(tǒng)中,當(dāng)多個(gè)管理員同時(shí)修改商品價(jià)格、處理訂單時(shí),系統(tǒng)可以為每個(gè)管理員的操作開啟一個(gè)線程,這些線程可以并發(fā)地訪問和修改數(shù)據(jù)庫(kù)中的數(shù)據(jù)(當(dāng)然,需要注意數(shù)據(jù)庫(kù)連接池的合理使用和數(shù)據(jù)的一致性問題),從而提高系統(tǒng)的并發(fā)處理能力。
4、系統(tǒng)監(jiān)控與日志記錄
背景和需求:
管理系統(tǒng)需要對(duì)自身的運(yùn)行狀態(tài)進(jìn)行監(jiān)控,如系統(tǒng)資源使用情況(CPU、內(nèi)存、磁盤 I/O 等)、服務(wù)的可用性等。同時(shí),需要記錄用戶操作日志、系統(tǒng)錯(cuò)誤日志等信息。
多線程的應(yīng)用方式:
可以使用一個(gè)線程專門負(fù)責(zé)系統(tǒng)監(jiān)控,定期收集系統(tǒng)狀態(tài)數(shù)據(jù)并進(jìn)行分析。另一個(gè)線程可以負(fù)責(zé)將日志信息寫入日志文件,這樣可以避免日志記錄操作阻塞其他業(yè)務(wù)操作,并且在系統(tǒng)出現(xiàn)問題時(shí)能夠及時(shí)記錄相關(guān)信息,方便后續(xù)的故障排查。
5、定時(shí)任務(wù)執(zhí)行
背景和需求:
管理系統(tǒng)中有很多定時(shí)任務(wù),如每天定時(shí)生成銷售報(bào)表、每月定時(shí)結(jié)算員工工資等。這些任務(wù)需要在特定的時(shí)間自動(dòng)執(zhí)行,并且不能影響系統(tǒng)的正常運(yùn)行。
多線程的應(yīng)用方式:
通過線程池來管理定時(shí)任務(wù)線程。例如,使用 Java 中的ScheduledExecutorService來安排定時(shí)任務(wù)。系統(tǒng)可以根據(jù)任務(wù)的執(zhí)行時(shí)間和頻率,在后臺(tái)線程中自動(dòng)執(zhí)行這些任務(wù),而不會(huì)干擾系統(tǒng)的日常操作。
Q:只要繼承了runnable接口,用這個(gè)類的時(shí)候就開啟多線程了嗎?
繼承Runnable接口只是定義了任務(wù)內(nèi)容
當(dāng)一個(gè)類實(shí)現(xiàn)了Runnable接口,它僅僅是定義了一個(gè)線程要執(zhí)行的任務(wù)邏輯,這個(gè)任務(wù)邏輯包含在run方法中。
例如:
class MyRunnable implements Runnable { @Override public void run() { System.out.println("執(zhí)行自定義任務(wù)"); } }
上述代碼定義了一個(gè)名為MyRunnable的類,它實(shí)現(xiàn)了Runnable接口,run方法里是具體要執(zhí)行的任務(wù)。但此時(shí),只是定義了任務(wù),還沒有開啟多線程。
需要通過Thread類來啟動(dòng)線程執(zhí)行任務(wù)
要真正開啟一個(gè)新的線程來執(zhí)行這個(gè)任務(wù),需要將Runnable對(duì)象作為參數(shù)傳遞給Thread類的構(gòu)造函數(shù),然后調(diào)用Thread對(duì)象的start方法。例如:
java public class Main { public static void main(String[] args) { MyRunnable runnable = new MyRunnable(); Thread thread = new Thread(runnable); thread.start(); } }
在這個(gè)例子中,首先創(chuàng)建了MyRunnable類的一個(gè)對(duì)象runnable,然后通過new Thread(runnable)創(chuàng)建了一個(gè)Thread對(duì)象,這個(gè)Thread對(duì)象將執(zhí)行runnable對(duì)象中定義的任務(wù)。最后,調(diào)用thread.start()方法來啟動(dòng)線程。如果只是創(chuàng)建了
Thread對(duì)象而沒有調(diào)用start方法,任務(wù)也不會(huì)在新的線程中執(zhí)行,和普通方法調(diào)用沒有區(qū)別。
所以,僅僅繼承Runnable接口并沒有開啟多線程,它只是提供了一種方便的方式來定義線程要執(zhí)行的任務(wù),還需要通過Thread類來實(shí)際啟動(dòng)線程,讓任務(wù)在新的線程環(huán)境中運(yùn)行。
總結(jié)
到此這篇關(guān)于JAVA多線程實(shí)現(xiàn)的四種方式及使用場(chǎng)景的文章就介紹到這了,更多相關(guān)JAVA多線程實(shí)現(xiàn)方式內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot引入Redis報(bào)Redis?command?timed?out兩種異常情況
這篇文章主要給大家介紹了關(guān)于SpringBoot引入Redis報(bào)Redis?command?timed?out兩種異常情況的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-08-08Springboot集成Kafka實(shí)現(xiàn)producer和consumer的示例代碼
這篇文章主要介紹了Springboot集成Kafka實(shí)現(xiàn)producer和consumer的示例代碼,詳細(xì)的介紹了什么是Kafka和安裝Kafka以及在springboot項(xiàng)目中集成kafka收發(fā)message,感興趣的小伙伴們可以參考一下2018-05-05解決Weblogic部署war找不到spring配置文件的問題
這篇文章主要介紹了解決Weblogic部署war找不到spring配置文件的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。2021-07-07mybatis的坑-integer類型為0的數(shù)據(jù)if?test失效問題
這篇文章主要介紹了mybatis的坑-integer類型為0的數(shù)據(jù)if?test失效問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01SpringBoot整合easyExcel實(shí)現(xiàn)CSV格式文件的導(dǎo)入導(dǎo)出
這篇文章主要為大家詳細(xì)介紹了SpringBoot整合easyExcel實(shí)現(xiàn)CSV格式文件的導(dǎo)入導(dǎo)出,文中的示例代碼講解詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴可以參考下2024-02-02