揭曉java啟動線程的方法「java啟動線程的幾種方式」

線程的創建及啟動方式
以下只展示較為常見的6中將建方式,更多完整的創建方式見下文:

import java.util.concurrent.*;

public class T02_HowToCreateThread {

/**

* 繼承 Thread, 重寫 run()

*/

static class MyThread extends Thread {

@Override

public void run() {

// super.run();

System.out.println(“Hello MyThread, extends Thread !”);

}

}

/**

* 實現 Runnable, 重寫run()

*/

static class MyRun implements Runnable {

@Override

public void run() {

System.out.println(“Hello MyRun, implements Runnable !”);

}

}

/**

* 實現 Callable, 重寫call()

*/

static class MyCallWithoutV implements Callable {

@Override

public Object call() throws Exception {

System.out.println(“Hello MyCallWithoutV, implements Callable”);

return “success, implements Callable<String>”;

}

}

/**

* 實現 Callable<V>, 重寫call()

*/

static class MyCallWithString implements Callable<String> {

@Override

public String call() throws Exception {

System.out.println(“Hello MyCallWithString, implements Callable<String>”);

return “success, implements Callable<String>”;

}

}

public static void main(String[] args) {

//第一種

new MyThread().start();

//第二種

new Thread(new MyRun()).start();

//第三種: Lambda表達式

new Thread(() -> {

System.out.println(“Hello Lambda !”);

}).start();

//第四種

//方式1:相當於繼承了Thread類,作為子類重寫run()實現

new Thread() {

public void run() {

System.out.println(“匿名內部類創建線程方式1…”);

};

}.start();

//方式2:實現Runnable,Runnable作為匿名內部類

new Thread(new Runnable() {

public void run() {

System.out.println(“匿名內部類創建線程方式2…”);

}

} ).start();

//第五種: FutureTask + Callable

new Thread(new FutureTask<String>(new MyCallWithoutV())).start();

new Thread(new FutureTask<>(new MyCallWithString())).start();

//第六種:
Executors.newCachedThreadPool()

ExecutorService executorService = Executors.newCachedThreadPool();

executorService.execute(() -> {

System.out.println(“Hello Executors.newCachedThreadPool and Lambda !”);

});

executorService.shutdown(); //優雅的關閉線程池, 用shutdown()

}

}

Hello 【Lambda】 !

【匿名內部類】創建線程方式2…

Hello MyThread, 【extends Thread】 !

【匿名內部類】創建線程方式1…

Hello MyRun, 【implements Runnable】 !

Hello MyCallWithoutV, 【implements Callable】

Hello MyCallWithString, 【implements Callable<返回值類型>】

【線程池實現】②
Executors.newCachedThreadPool and Lambda !

【定時器方式】定時任務延遲0(即立刻執行),每隔1000ms執行一次

【線程池實現】①
Executors.newFixedThreadPool(線程數), 線程pool-1-thread-1 is running

【線程池實現】①
Executors.newFixedThreadPool(線程數), 線程pool-1-thread-1 is running

【線程池實現】①
Executors.newFixedThreadPool(線程數), 線程pool-1-thread-1 is running

【線程池實現】①
Executors.newFixedThreadPool(線程數), 線程pool-1-thread-1 is running

【線程池實現】①
Executors.newFixedThreadPool(線程數), 線程pool-1-thread-1 is running

【線程池實現】①
Executors.newFixedThreadPool(線程數), 線程pool-1-thread-4 is running

【線程池實現】①
Executors.newFixedThreadPool(線程數), 線程pool-1-thread-1 is running

【線程池實現】①
Executors.newFixedThreadPool(線程數), 線程pool-1-thread-3 is running

【線程池實現】①
Executors.newFixedThreadPool(線程數), 線程pool-1-thread-2 is running

【線程池實現】①
Executors.newFixedThreadPool(線程數), 線程pool-1-thread-5 is running

【定時器方式】定時任務延遲0(即立刻執行),每隔1000ms執行一次

【定時器方式】定時任務延遲0(即立刻執行),每隔1000ms執行一次

……

……

【定時器方式】定時任務延遲0(即立刻執行),每隔1000ms執行一次

8種創建方式

1、繼承 Thread 類

extends Thread, @Override run()

無返回值、無法拋出異常

創建: 編寫一個類 MyThread 讓它繼承 Thread 類,並把需要多線程運行的程序放到 public void run() 方法里。

啟動: 在主函數中,new 出 MyThread 類的實例。

運行: 調用 MyThread 類的實例的 start() 方法即可。

/**

* 繼承 Thread, 重寫 run()

*/

static class MyThread extends Thread {

@Override

public void run() {

super.run();

System.out.println(“Hello MyThread, 【extends Thread】 !”);

}

}

public static void main(String[] args) {

new MyThread().start();

}

2、實現 Runnable 介面

implements Runnable, @Override run()

無返回值、無法拋出異常

創建: 編寫一個類 MyThread 讓它實現 Runnable 介面,並且要重寫 run() 方法(把需要多線程運行的程序放到 public void run() 方法里)。

啟動: 在主函數中,new 出 MyThread 類的實例,new 出Thread 類(帶有 target 的構造方法),把MyThread 類的實例作為參數傳入Thread 類的構造方法里。

運行: 調用 Thread 類的實例的 start() 方法即可。

/**

* 實現 Runnable介面, 重寫run()

*/

static class MyRun implements Runnable {

@Override

public void run() {

System.out.println(“Hello MyRun, 【implements Runnable】 !”);

}

}

public static void main(String[] args) {

new Thread(new MyRun()).start();

}

3、Lambda 表達式

書寫簡便

可以拋出異常(需要有try\catch)

public static void main(String[] args) {

new Thread(() -> {

System.out.println(“Hello 【Lambda】 !”);

}).start();

}

4、匿名內部類的方式(2種方式)

適用於創建啟動線程次數較少的環境,書寫更加簡便

無返回值、無法拋出異常

2種方式

方式①相當於繼承了Thread類,作為子類重寫run()實現

方式②相當於實現了Runnable介面,Runnable作為匿名內部類

public static void main(String[] args) {

//方式1: 相當於繼承了Thread類,作為子類重寫run()實現

new Thread() {

public void run() {

System.out.println(“【匿名內部類】創建線程方式1…”);

};

}.start();

//方式2: 相當於實現了Runnable介面,Runnable作為匿名內部類

new Thread(new Runnable() {

public void run() {

System.out.println(“【匿名內部類】創建線程方式2…”);

}

} ).start();

}

5、FutureTask + Callable

implements Callable<返回值類型>, @Override call()

帶返回值、可拋出異常

創建: 編寫一個類 MyThread 讓它實現 Callable 介面,並且實現 call() 方法,注意 call() 方法是有返回值的。

啟動: new 出Callable 介面的實現類MyCallable,new 出 FutureTask 類的實例 task,把call() 方法的返回值放入FutureTask 類的構造方法里,把 task 放入 new 出的 Thread 構造方法里。

運行: 調用 Thread 類的實例的 start() 方法即可。

public static void main(String[] args) {

new Thread(new FutureTask<String>(new MyCallWithoutV())).start();

new Thread(new FutureTask<>(new MyCallWithString())).start();

}

6、線程池的實現(2種方式)

降低了創建線程和銷毀線程的時間開銷

減少了資源浪費

返回的實際上是ExecutorService,而ExecutorService是Executor的子介面

方式①
Executors.newFixedThreadPool(線程數)

方式②
Executors.newCachedThreadPool()

public static void main(String[] args) {

//方式1
Executors.newFixedThreadPool(線程數)

//創建帶有5個線程的線程池

ExecutorService threadPool_1 = Executors.newFixedThreadPool(5);

for(int i = 0 ;i < 10 ; i++) {

threadPool_1.execute(new Runnable() {

public void run() {

System.out.println(“【線程池實現】①
Executors.newFixedThreadPool(線程數), 線程”+Thread.currentThread().getName()+” is running”);

}

});

}

threadPool_1.shutdown(); //優雅的關閉線程池, 用shutdown()

//方式2
Executors.newCachedThreadPool()

ExecutorService threadPool_2 = Executors.newCachedThreadPool();

threadPool_2.execute(() -> {

System.out.println(“【線程池實現】②
Executors.newCachedThreadPool and Lambda !”);

});

threadPool_2.shutdown(); //優雅的關閉線程池, 用shutdown()

}

值得注意的是:

方式2創建的是CachedThreadPool則不需要指定線程數量,線程數量多少取決於線程任務,不夠用則創建線程,夠用則回收。

這2種方式都有個很關鍵的問題,那就是:如果缺少了 shutdown() 銷毀線程池的話,即使程序運行完畢了,但是程序並沒有停止, 原因是 線程池沒有被銷毀。 如果沒有銷毀線程池,會浪費資源的。一般會選擇shutdown()來優雅關閉線程池的。

無論是哪種方式,返回的類型都是ExecutorService。作為Executor的子介面的ExecutorService才有shutdown()方法。

如果返回的是Executor類型,那麼是沒有shutdown()方法的。

Executor只有一個方法,那就是void execute(Runnable command)。

線程池的關閉方法(3個)

ExecutorService里有下面這3種關閉方法:

shutdown():停止接收新任務,原來的任務繼續執行

shutdownNow():停止接收新任務,原來的任務停止執行

awaitTermination(long timeOut, TimeUnit unit):當前線程阻塞

關閉功能「從強到弱」依次是:shuntdownNow() > shutdown() > awaitTermination()

1、優雅的關閉,用 shutdown()

2、想立馬關閉,並得到未執行任務列表,用 shutdownNow()

3、優雅的關閉,並允許關閉聲明後新任務能提交,用 awaitTermination()

線程池的種類(4種)

這裡只展示了FixedThreadPool和CachedThreadPool這兩種線程池,但是,還有其他兩種為SingleThreadPool和ScheduledThreadPool。

01.SingleThreadPool

該線程池只有一條線程來執行任務

應用場景:有順序的任務、任務量少,並且不需要並發執行

02.CachedThreadPool

可緩存的線程池

應用場景:耗時少、任務量大、處理任務速度 > 提交任務速度

03.FixedThreadPool

可重用的定長(固定線程數)的線程池

可控制線程最大並發數,超出的線程會在隊列中等待

可以通過控制最大線程來使伺服器達到最大的使用率,又可以保證即時流量的突然增大也不會佔用伺服器過多的資源。

應用場景:有其他條件限制或固定要求的任務量

04.ScheduledThreadPool

周期性執行任務的線程池

應用場景:執行周期性任務

7、定時器的方式

Java提供了定時器Timer,但是自帶的定時器有不可控的缺點

這種方式,當任務未執行完畢或我們每次想執行不同任務的時候,實現起來比較麻煩

建議使用作業調度框架quartz

import java.util.Timer;

import java.util.TimerTask;

public class CreateThreadUseTimer {

public static void main(String[] args) {

Timer timer = new Timer();

timer.schedule(new TimerTask() {

@Override

public void run() {

System.out.println(“定時任務延遲0(即立刻執行),每隔1000ms執行一次”);

}

}, 0, 1000);

}

}

8、Spring實現多線程

1.Spring通過任務執行器TaskExecutor來實現多線程和並發編程。

2.使用TreadPoolTaskExecutor可實現一個基於線程池的TaskExecutor。

3.實際開發任務一般是非阻礙的,即非同步的,所以我們要在配置類中通過@EnableAsync開啟對非同步任務的支持,並通過在實際執行的Bean中的方法使用@Async註解來聲明這是一個非同步任務。

0. 同步和非同步

同步交互:指發送一個請求,需要等待返回,然後才能夠發送下一個請求,有個等待過程;

非同步交互:指發送一個請求,不需要等待返回,隨時可以再發送下一個請求,即不需要等待。

區別:一個需要等待,一個不需要等待。在部分情況下,我們的項目開發中都會優先選擇不需要等待的非同步交互方式。

1. 引入 Maven 依賴

<dependencies>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter</artifactId>

<version>2.4.4</version>

</dependency>

</dependencies>

2. 非同步執行的配置類 AsyncConfig

package com.melodyjerry.thread;

import org.springframework.context.annotation.ComponentScan;

import org.springframework.context.annotation.Configuration;

import org.springframework.scheduling.annotation.AsyncConfigurer;

import org.springframework.scheduling.annotation.EnableAsync;

import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;

/**

* @classname AsyncConfig

* @description 開啟非同步執行的配置類

*/

@Configuration

@EnableAsync //開啟非同步執行

@ComponentScan(“com.melodyjerry.thread”)

public class AsyncConfig implements AsyncConfigurer {

@Override

public Executor getAsyncExecutor() {

ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();

//線程池中的線程的名稱前綴

threadPoolTaskExecutor.setThreadNamePrefix(“SpringBoot線程池的前綴-“);

//線程池的核心線程數大小

threadPoolTaskExecutor.setCorePoolSize(4);

//線程池的最大線程數

threadPoolTaskExecutor.setMaxPoolSize(8);

//等待隊列的大小

threadPoolTaskExecutor.setQueueCapacity(25);

//執行初始化

threadPoolTaskExecutor.initialize();

return threadPoolTaskExecutor;

}

}

3. 非同步任務的執行類

package com.melodyjerry.thread;

import org.springframework.scheduling.annotation.Async;

import org.springframework.stereotype.Service;

/**

* @classname AsyncTaskService

* @description 非同步任務的執行類

*/

@Service

public class AsyncTaskService {

@Async //非同步方法

public void executeAsyncTask(Integer i) {

System.out.println(“執行非同步任務: “+i);

}

@Async //非同步方法

public void executeAsyncTaskPlus(Integer i) {

System.out.println(“執行非同步任務+1: ” + (i+1));

}

}

@Async註解表明該方法是個非同步方法。

從Async註解介面可以看到,Target即可以在方法也可以在類型上,如果註解在類型上,表明該類所有的方法都是非同步方法。

4. 測試效果 TestThreadApplication

package com.melodyjerry.thread;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.context.ConfigurableApplicationContext;

/**

* @classname TestThreadApplication

* @description 測試非同步任務

*/

@SpringBootApplication

public class TestThreadApplication {

public static void main(String[] args) {

ConfigurableApplicationContext context = SpringApplication.run(TestThreadApplication.class, args);

AsyncTaskService asyncTaskService = context.getBean(AsyncTaskService.class);

for (int i = 0; i < 10; i++) {

asyncTaskService.executeAsyncTask(i);

asyncTaskService.executeAsyncTaskPlus(i);

}

System.out.println(“This Program has Begun successfully”);

}

}

This Program has Begun successfully

執行非同步任務: 0

執行非同步任務: 2

執行非同步任務+1: 1

執行非同步任務+1: 3

執行非同步任務: 3

執行非同步任務: 4

執行非同步任務+1: 5

執行非同步任務: 5

執行非同步任務+1: 6

執行非同步任務+1: 2

執行非同步任務: 6

執行非同步任務+1: 4

執行非同步任務: 7

執行非同步任務: 8

執行非同步任務+1: 9

執行非同步任務: 9

執行非同步任務+1: 10

執行非同步任務+1: 7

執行非同步任務+1: 8

執行非同步任務: 1

4種啟動方式

1.new Thread().start();

2.new Thread(Runnable).start();

3.Executors.newCachedThreadPool().execute()

注意:線程池的關閉

4.FutureTask + Callable

能直接調用Thread類的run()方法?

當然可以直接調用,但是如果我們調用了Thread的run()方法,它的行為就會和普通的方法一樣,是在當前線程執行

為了在新的線程中執行我們的代碼,必須使用Thread.start()方法。

原創文章,作者:投稿專員,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/281402.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
投稿專員的頭像投稿專員
上一篇 2024-12-21 13:17
下一篇 2024-12-21 13:17

相關推薦

發表回復

登錄後才能評論