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 的類
③ 代碼可以被多個線程共享,代碼和數(shù)據(jù)獨(dú)立,適合多個相同的程序代碼的線程去處理同一個資源的情況
實(shí)現(xiàn)Callable接口
實(shí)現(xiàn)步驟
1、定義一個線程任務(wù)類實(shí)現(xiàn)Callable接口,聲明線程執(zhí)行的結(jié)果類型。
2、重寫線程任務(wù)類的call()方法,這個方法可以直接返回執(zhí)行的結(jié)果。
3、創(chuàng)建一個Callable的線程任務(wù)對象。
4、把Callable的線程任務(wù)對象包裝成一個未來任務(wù)對象。
5、把未來任務(wù)對象包裝成線程對象。
6、調(diào)用線程start()方法,啟動線程。
7、獲取線程執(zhí)行結(jié)果。
代碼:
/**
* @author gf
* @date 2023/2/22
*/
// 1、定義一個線程任務(wù)類實(shí)現(xiàn)Callable接口,聲明線程執(zhí)行的結(jié)果類型。
public class CallableTicket implements Callable<Object > {
private int ticket=20;
// 2、重寫線程任務(wù)類的call()方法,這個方法可以直接返回執(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)建一個Callable的線程任務(wù)對象。
CallableTicket callableTicket = new CallableTicket();
// 4、把Callable的線程任務(wù)對象包裝成一個未來任務(wù)對象。
FutureTask futureTask = new FutureTask(callableTicket);
// 5、把未來任務(wù)對象包裝成線程對象。
Thread thread = new Thread(futureTask);
// 6、調(diào)用線程start()方法,啟動線程。
thread.start();
// 7、獲取線程執(zhí)行結(jié)果。如果此時獲取結(jié)果的任務(wù)還未執(zhí)行完成,會讓出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í)現(xiàn)了Runable,F(xiàn)uture接口。它既可以作為Runnable被線程執(zhí)行,又可以作為Future得到Callable的返回值
使用線程池
通過Executors創(chuàng)建
它提供了四種線程池:
- newCachedThreadPool創(chuàng)建一個可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閑線程,若無可回收,則新建線程。
- newFixedThreadPool 創(chuàng)建一個定長線程池,可控制線程最大并發(fā)數(shù),超出的線程會在隊列中等待。
- newScheduledThreadPool 創(chuàng)建一個定長線程池,支持定時及周期性任務(wù)執(zhí)行。
- newSingleThreadExecutor 創(chuàng)建一個單線程化的線程池,它只會用唯一的工作線程來執(zhí)行任務(wù),保證所有任務(wù)按照指定順序(FIFO, LIFO, 優(yōu)先級)執(zhí)行。
public class Demo01 {
public static void main(String[] args) {
//ExecutorService threadPool = Executors.newSingleThreadExecutor(); //創(chuàng)建單個線程
//ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(5);//創(chuàng)建一個固定大小的線程池
//ExecutorService threadPool = Executors.newFixedThreadPool(5); //創(chuàng)建一個固定大小的線程池
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)建線程池。因為newFixedThreadPool 和newSingleThreadExecutor允許的最大請求隊列長度為Integer.MAX_VALUE,可能會堆積大量的請求,從而導(dǎo)致OOM。newCachedThreadPoo和newScheduledThreadPool允許的創(chuàng)建線程的最大數(shù)量為Integer.MAX_VALUE,,從而導(dǎo)致OOM。
通過ThreadPoolExecutor創(chuàng)建
ThreadPoolExecutor有7個核心參數(shù)
- ThreadPoolExecutor(int corePoolSize, //核心線程池大小,始終存在
- int maximumPoolSize, //最大線程數(shù)
- long keepAliveTime, //空閑線程等待時間,超時則銷毀
- TimeUnit unit, //時間單位
- BlockingQueue<Runnable> workQueue, //等待阻塞隊列
- ThreadFactory threadFactory, //線程工廠
- RejectedExecutionHandler handler) //線程拒絕策略
其最后一個參數(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密集型,邏輯處理器個數(shù)
//2.io密集型 > 判斷程序十分耗IO的線程,最大線程池大小應(yīng)該比這個大
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ù)時,不需要去創(chuàng)建線程再來執(zhí)行,只要調(diào)動現(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)文章
Java命令行運(yùn)行錯誤之找不到或無法加載主類問題的解決方法
這篇文章主要給大家介紹了關(guān)于Java命令行運(yùn)行錯誤之找不到或無法加載主類問題的解決方法,文中通過實(shí)例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2022-01-01
mybatis實(shí)現(xiàn)動態(tài)升降序的問題小結(jié)
文章介紹了如何在MyBatis的XML文件中實(shí)現(xiàn)動態(tài)排序,使用$符號而不是#符號來引用變量,以避免SQL注入,同時,強(qiáng)調(diào)了在Java代碼中進(jìn)行防注入處理的重要性,感興趣的朋友一起看看吧2025-02-02
java實(shí)現(xiàn)fibonacci數(shù)列學(xué)習(xí)示例分享(斐波那契數(shù)列)
這篇文章主要介紹了fibonacci數(shù)列(斐波那契數(shù)列)示例,大家參考使用吧2014-01-01

