Java中創(chuàng)建線程的四種方法解析
前言
在 Java 中,實(shí)現(xiàn)多線程的主要有以下四種
- 繼承 Thread 類,重寫 run() 方法;
- 實(shí)現(xiàn) Runnable 接口,實(shí)現(xiàn) run() 方法,并將 Runnable 實(shí)現(xiàn)類的實(shí)例作為 Thread 構(gòu)造函數(shù)的參數(shù) target;
- 實(shí)現(xiàn) Callable 接口,實(shí)現(xiàn) call() 方法,然后通過 FutureTask 包裝器來創(chuàng)建 Thread 線程;
- 通過 ThreadPoolExecutor 創(chuàng)建線程池,并從線程池中獲取線程用于執(zhí)行任務(wù);
繼承Thread類
創(chuàng)建步驟:
- 定義類繼承Thread;
- 重寫Thread類中的run方法;
- 實(shí)例化線程對象;
- 調(diào)用線程的start方法開啟線程;
代碼:
package com.scg.springcloudordercenter.controller; // 1.繼承Thread類 public class ThreadDemo extends Thread{ private int ticket=20; // 2.重寫run 方法 public void run(){ for(int i=0;i<20;i++){ if(this.ticket>0){ System.out.println("剩余票數(shù)=="+ticket--); } } } } class ThreadTest { public static void main(String[] args) { // 3.實(shí)例化線程對象 ThreadDemo md = new ThreadDemo() ; // 4.調(diào)用start()開啟線程 md.start(); } }
通過實(shí)現(xiàn) Runnable 接口
創(chuàng)建步驟:
- 實(shí)現(xiàn)Runnable接口;
- 重寫Run()方法;
- 實(shí)例化實(shí)現(xiàn)類;
- 將實(shí)例化線程類轉(zhuǎn)化為線程對象;
- 開啟線程 調(diào)用start()方法;
代碼
package com.scg.springcloudordercenter.controller; // 1.實(shí)現(xiàn)Runnable類 public class RunnableDemo implements Runnable{ private int ticket=20; // 2.重寫run 方法 public void run(){ for(int i=0;i<20;i++){ if(this.ticket>0){ System.out.println("剩余票數(shù)=="+ticket--); } } } } class RunnableTest { public static void main(String[] args) { // 3.實(shí)例化實(shí)現(xiàn)類 RunnableDemo rd = new RunnableDemo() ; // 4.將實(shí)例化實(shí)現(xiàn)類轉(zhuǎn)化為線程對象 Thread thread = new Thread(rd); // 5.開啟線程 thread.start(); } }
實(shí)現(xiàn) Runnable 接口比繼承 Thread 類所具有的優(yōu)勢主要有:
① 可以避免 JAVA 中單繼承的限制;
② 線程池只能放入實(shí)現(xiàn) Runable 或 Callable類線程,不能直接放入繼承 Thread 的類
③ 代碼可以被多個(gè)線程共享,代碼和數(shù)據(jù)獨(dú)立,適合多個(gè)相同的程序代碼的線程去處理同一個(gè)資源的情況
實(shí)現(xiàn)Callable接口
實(shí)現(xiàn)步驟
1、定義一個(gè)線程任務(wù)類實(shí)現(xiàn)Callable接口,聲明線程執(zhí)行的結(jié)果類型。
2、重寫線程任務(wù)類的call()方法,這個(gè)方法可以直接返回執(zhí)行的結(jié)果。
3、創(chuàng)建一個(gè)Callable的線程任務(wù)對象。
4、把Callable的線程任務(wù)對象包裝成一個(gè)未來任務(wù)對象。
5、把未來任務(wù)對象包裝成線程對象。
6、調(diào)用線程start()方法,啟動(dòng)線程。
7、獲取線程執(zhí)行結(jié)果。
代碼:
/** * @author gf * @date 2023/2/22 */ // 1、定義一個(gè)線程任務(wù)類實(shí)現(xiàn)Callable接口,聲明線程執(zhí)行的結(jié)果類型。 public class CallableTicket implements Callable<Object > { private int ticket=20; // 2、重寫線程任務(wù)類的call()方法,這個(gè)方法可以直接返回執(zhí)行的結(jié)果。 @Override public Object call() throws Exception { int sum = 0; for (int i = 1; i <= 100; i++) { if (i % 2 == 0) { sum += i; } } return sum; } }
package com.scg.springcloudordercenter.controller; import java.util.concurrent.FutureTask; /** * @author gf * @date 2023/2/22 */ public class CallableMain { public static void main(String[] args) { // 3、創(chuàng)建一個(gè)Callable的線程任務(wù)對象。 CallableTicket callableTicket = new CallableTicket(); // 4、把Callable的線程任務(wù)對象包裝成一個(gè)未來任務(wù)對象。 FutureTask futureTask = new FutureTask(callableTicket); // 5、把未來任務(wù)對象包裝成線程對象。 Thread thread = new Thread(futureTask); // 6、調(diào)用線程start()方法,啟動(dòng)線程。 thread.start(); // 7、獲取線程執(zhí)行結(jié)果。如果此時(shí)獲取結(jié)果的任務(wù)還未執(zhí)行完成,會(huì)讓出CPU,直至任務(wù)執(zhí)行完成才獲取結(jié)果。 try { //6.獲取Callable中call方法的返回值 //get()返回值即為FutureTask構(gòu)造器參數(shù)Callable實(shí)現(xiàn)類重寫的call()方法返回值。 Object sum = futureTask.get(); System.out.println("總和為:"+sum); } catch (Exception e) { e.printStackTrace(); } } }
優(yōu)點(diǎn):
與使用Runnable相比,Callable功能更強(qiáng)大
- 相比run方法,可以有返回值。
- 方法可以拋異常。
- 支持泛型的返回值。
- 需要借助FutureTask類,比如獲取返回結(jié)果。
Future接口
- 可以對具體Runnable、Callable任務(wù)的執(zhí)行結(jié)果進(jìn)行取消、查詢是否完成、獲取結(jié)果等。
- FutureTask是Future接口的唯一實(shí)現(xiàn)類。
- FutureTask同時(shí)實(shí)現(xiàn)了Runable,F(xiàn)uture接口。它既可以作為Runnable被線程執(zhí)行,又可以作為Future得到Callable的返回值
使用線程池
通過Executors創(chuàng)建
它提供了四種線程池:
- newCachedThreadPool創(chuàng)建一個(gè)可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閑線程,若無可回收,則新建線程。
- newFixedThreadPool 創(chuàng)建一個(gè)定長線程池,可控制線程最大并發(fā)數(shù),超出的線程會(huì)在隊(duì)列中等待。
- newScheduledThreadPool 創(chuàng)建一個(gè)定長線程池,支持定時(shí)及周期性任務(wù)執(zhí)行。
- newSingleThreadExecutor 創(chuàng)建一個(gè)單線程化的線程池,它只會(huì)用唯一的工作線程來執(zhí)行任務(wù),保證所有任務(wù)按照指定順序(FIFO, LIFO, 優(yōu)先級)執(zhí)行。
public class Demo01 { public static void main(String[] args) { //ExecutorService threadPool = Executors.newSingleThreadExecutor(); //創(chuàng)建單個(gè)線程 //ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(5);//創(chuàng)建一個(gè)固定大小的線程池 //ExecutorService threadPool = Executors.newFixedThreadPool(5); //創(chuàng)建一個(gè)固定大小的線程池 ExecutorService threadPool = Executors.newCachedThreadPool(); //創(chuàng)建大小可伸縮的線程池 try { for (int i = 0; i < 30; i++) { //使用線程池來創(chuàng)建線程 threadPool.execute(()->{ System.out.println(Thread.currentThread().getName()+"ok"); }); } } catch (Exception e) { e.printStackTrace(); } finally { threadPool.shutdown(); //線程池使用完畢后需要關(guān)閉 } } }
注意:不建議使用這種方法創(chuàng)建線程池。因?yàn)閚ewFixedThreadPool 和newSingleThreadExecutor允許的最大請求隊(duì)列長度為Integer.MAX_VALUE,可能會(huì)堆積大量的請求,從而導(dǎo)致OOM。newCachedThreadPoo和newScheduledThreadPool允許的創(chuàng)建線程的最大數(shù)量為Integer.MAX_VALUE,,從而導(dǎo)致OOM。
通過ThreadPoolExecutor創(chuàng)建
ThreadPoolExecutor有7個(gè)核心參數(shù)
- ThreadPoolExecutor(int corePoolSize, //核心線程池大小,始終存在
- int maximumPoolSize, //最大線程數(shù)
- long keepAliveTime, //空閑線程等待時(shí)間,超時(shí)則銷毀
- TimeUnit unit, //時(shí)間單位
- BlockingQueue<Runnable> workQueue, //等待阻塞隊(duì)列
- ThreadFactory threadFactory, //線程工廠
- RejectedExecutionHandler handler) //線程拒絕策略
其最后一個(gè)參數(shù)拒絕策略共有四種:
- new ThreadPoolExecutor.AbortPolicy():達(dá)到最大承載量,不再處理,并且拋出異常
- new ThreadPoolExecutor.CallerRunsPolicy():達(dá)到最大承載量,從哪來的去哪里
- new ThreadPoolExecutor.DiscardPolicy():達(dá)到最大承載量,丟掉任務(wù),但不拋出異常
- new ThreadPoolExecutor.DiscardOldestPolicy():達(dá)到最大承載量,嘗試與最早執(zhí)行的線程去競爭,不拋出異常
public class Demo02 { public static void main(String[] args) { //new ThreadPoolExecutor.AbortPolicy():達(dá)到最大承載量,不再處理,并且拋出異常 //new ThreadPoolExecutor.CallerRunsPolicy():達(dá)到最大承載量,從哪來的去哪里 //new ThreadPoolExecutor.DiscardPolicy():達(dá)到最大承載量,丟掉任務(wù),但不拋出異常 //new ThreadPoolExecutor.DiscardOldestPolicy():達(dá)到最大承載量,嘗試與最早執(zhí)行的線程去競爭,不拋出異常 //最大線程池大小該如何定義 //1.cpu密集型,邏輯處理器個(gè)數(shù) //2.io密集型 > 判斷程序十分耗IO的線程,最大線程池大小應(yīng)該比這個(gè)大 int maxPools= Runtime.getRuntime().availableProcessors(); System.out.println(maxPools); ThreadPoolExecutor threadPool = new ThreadPoolExecutor( 2, maxPools, 3, TimeUnit.SECONDS, new ArrayBlockingQueue<>(3), Executors.defaultThreadFactory(), new ThreadPoolExecutor.DiscardOldestPolicy() ); try { for (int i = 0; i < 10; i++) { //使用線程池來創(chuàng)建線程 //最大承載:maximumPoolSize+workQueue,超過執(zhí)行拒絕策略 threadPool.execute(()->{ System.out.println(Thread.currentThread().getName()+" ok"); }); } } catch (Exception e) { e.printStackTrace(); } finally { threadPool.shutdown(); //線程池使用完畢后需要關(guān)閉 } } }
使用線程池的優(yōu)點(diǎn);
1.減少資源的消耗。重復(fù)利用已經(jīng)創(chuàng)建的線程,避免頻繁的創(chuàng)造和銷毀線程,減少消耗。
2.提高響應(yīng)速度。當(dāng)執(zhí)行任務(wù)時(shí),不需要去創(chuàng)建線程再來執(zhí)行,只要調(diào)動(dòng)現(xiàn)有的線程來執(zhí)行即可。
3.提高了線程的管理性。線程是稀缺資源,使用線程池可以進(jìn)行統(tǒng)一的分配、調(diào)優(yōu)和監(jiān)控。
到此這篇關(guān)于Java中創(chuàng)建線程的四種方法解析的文章就介紹到這了,更多相關(guān)Java創(chuàng)建線程內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Maven+oracle+SSM搭建簡單項(xiàng)目的方法
本篇文章主要介紹了Maven+oracle+SSM搭建簡單項(xiàng)目的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-03-03Java日常練習(xí)題,每天進(jìn)步一點(diǎn)點(diǎn)(51)
下面小編就為大家?guī)硪黄狫ava基礎(chǔ)的幾道練習(xí)題(分享)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧,希望可以幫到你2021-08-08一文教你掌握J(rèn)ava如何實(shí)現(xiàn)判空
實(shí)際項(xiàng)目中我們會(huì)有很多地方需要判空校驗(yàn),如果不做判空校驗(yàn)則可能產(chǎn)生NullPointerException異常。所以本文小編為大家整理了Java中幾個(gè)常見的判空方法,希望對大家有所幫助2023-04-04Java利用httpclient通過get、post方式調(diào)用https接口的方法
這篇文章主要介紹了Java利用httpclient通過get、post方式調(diào)用https接口的方法,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-02-02Kafka是什么及如何使用SpringBoot對接Kafka(最新推薦)
這篇文章主要介紹了Kafka是什么,以及如何使用SpringBoot對接Kafka,今天我們通過一個(gè)Demo講解了在SpringBoot中如何對接Kafka,也介紹了下關(guān)鍵類?KafkaTemplate,需要的朋友可以參考下2023-11-11使用maven項(xiàng)目pom.xml文件配置打包功能和靜態(tài)資源文件自帶版本號功能
在Maven項(xiàng)目中,通過pom.xml文件配置打包功能,可以控制構(gòu)建過程,生成可部署的包,同時(shí),為了緩存控制與版本更新,可以在打包時(shí)給靜態(tài)資源文件如JS、CSS添加版本號,這通常通過插件如maven-resources-plugin實(shí)現(xiàn)2024-09-09Java中List對象集合按對象中某字段進(jìn)行排序舉例
這篇文章主要給大家介紹了關(guān)于Java中List對象集合按對象中某字段進(jìn)行排序的相關(guān)資料,我們在日常開發(fā)中也經(jīng)常會(huì)用到排序算法,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-07-07