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