JUC多线程:创建线程的四种方式
在 Java 中,實現多線程的主要有以下四種:
(1)繼承 Thread 類,重寫 run() 方法;
(2)實現 Runnable 接口,實現?run() 方法,并將 Runnable 實現類的實例作為 Thread 構造函數的參數 target;
(3)實現 Callable 接口,實現 call() 方法,然后通過 FutureTask 包裝器來創建 Thread 線程;
(4)通過 ThreadPoolExecutor 創建線程池,并從線程池中獲取線程用于執行任務;
第(1)(2)種方式無法獲取線程的執行結果,因為通過重寫的 run()? 方法的返回值是void;第(3)種方式可以獲取線程的執行結果,因為通過 Callable 接口的 call() 方法的返回值是 Object,可以將返回的結果可以放在 Object 對象中;第(4)種方式對于兩種情況都支持,具體取決于任務的類型,有返回值的任務必須實現 Callable 接口,無返回值的任務必須實現 Runnable 接口。
1、繼承Thread類的方式:
Thread 實現了 Runnable 接口,代表一個線程的實例。啟動線程的唯一方法就是通過 Thread 類的start() 方法。start() 方法是一個native方法,它將啟動一個新線程,并執行run()方法。
這種方式實現多線程很簡單,直接 extends?Thread,并重寫 run() 方法,就可以啟動新線程執行自己定義的run()方法。例如:
public class MyThread extends Thread { public void run() { System.out.println("MyThread.run()"); } } MyThread myThread1 = new MyThread(); MyThread myThread2 = new MyThread(); myThread1.start(); myThread2.start();2、通過實現 Runnable 接口:
通過實現 Runnable 接口,實現 run() 方法,將 Runnable? 接口的實現類的實例作為 Thread 的帶參構造函數中,并通過調用 start() 方法啟動線程,如下:
public class ThreadDemo02 {public static void main(String[] args){ System.out.println(Thread.currentThread().getName());Thread t1 = new Thread(new MyThread());t1.start(); } } class MyThread implements Runnable{@Overridepublic void run() {System.out.println(Thread.currentThread().getName()+"-->我是通過實現接口的線程實現方式!");} }實現 Runnable 接口比繼承 Thread 類所具有的優勢主要有:
- ① 可以避免 JAVA 中單繼承的限制;
- ② 線程池只能放入實現 Runable 或 Callable類線程,不能直接放入繼承 Thread 的類
- ③ 代碼可以被多個線程共享,代碼和數據獨立,適合多個相同的程序代碼的線程去處理同一個資源的情況
3、實現Callable接口,并通過FutureTask包裝器來創建Thread線程:
(1)實現?Callable 接口,并實現 call() 方法;?
(2)創建 Callable 接口的實現類的實例,使用 FutureTask 類包裝 Callable 對象,該 FutureTask 對象封裝了 Callable 對象的 call() 方法的返回值;?
(3)使用 FutureTask 對象作為 Thread 類的構造函數的?target 參數創建并啟動線程;
(4)調用 FutureTask 對象的 get() 來獲取子線程執行結束的返回值;
public class ThreadDemo03 {public static void main(String[] args) {Callable<Object> oneCallable = new Tickets<Object>();FutureTask<Object> oneTask = new FutureTask<Object>(oneCallable);Thread t = new Thread(oneTask);System.out.println(Thread.currentThread().getName());t.start();} } class Tickets<Object> implements Callable<Object>{//重寫call方法@Overridepublic Object call() throws Exception {System.out.println(Thread.currentThread().getName()+"-->我是通過實現Callable接口通過FutureTask包裝器來實現的線程");return null;} }4、使用 ThreadPoolExecutor 創建線程池:
使用 ThreadPoolExecutor 創建線程池,并從線程池中獲取線程用于執行任務。在 JUC 中,Executor 框架已經實現了幾種線程池,我們就以?Executor 的 newFixedThreadPool 來作為 Demo 的展示。
import java.util.concurrent.*; import java.util.Date; import java.util.List; import java.util.ArrayList; /** * 有返回值的線程 */ @SuppressWarnings("unchecked") public class Test { public static void main(String[] args) throws ExecutionException, InterruptedException { System.out.println("----程序開始運行----"); Date date1 = new Date(); int taskSize = 5; // 創建一個線程池 ExecutorService pool = Executors.newFixedThreadPool(taskSize); // 創建多個有返回值的任務 List<Future> list = new ArrayList<Future>(); for (int i = 0; i < taskSize; i++) { Callable c = new MyCallable(i + " "); // 執行任務并獲取Future對象 Future f = pool.submit(c); // System.out.println(">>>" + f.get().toString()); list.add(f); } // 關閉線程池 pool.shutdown(); // 獲取所有并發任務的運行結果 for (Future f : list) { // 從Future對象上獲取任務的返回值,并輸出到控制臺 System.out.println(">>>" + f.get().toString()); } Date date2 = new Date(); System.out.println("----程序結束運行----,程序運行時間【" + (date2.getTime() - date1.getTime()) + "毫秒】"); } } class MyCallable implements Callable<Object> { private String taskNum; MyCallable(String taskNum) { this.taskNum = taskNum; } public Object call() throws Exception { System.out.println(">>>" + taskNum + "任務啟動"); Date dateTmp1 = new Date(); Thread.sleep(1000); Date dateTmp2 = new Date(); long time = dateTmp2.getTime() - dateTmp1.getTime(); System.out.println(">>>" + taskNum + "任務終止"); return taskNum + "任務返回運行結果,當前任務時間【" + time + "毫秒】"; } }ExecutorService、Callable、Future 實際上都是屬于 Executor 框架。線程池支持有返回結果和無返回結果的任務,有返回值的任務必須實現Callable接口,無返回值的任務必須實現Runnable接口。對于有結果的任務,執行 Callable 任務后,可以獲取一個 Future 的對象,在該對象上調用 get 就可以獲取到Callable任務返回的 Object 了,但需要注意的是:get方法是阻塞的,如果線程未返回結果,那么 get() 方法會一直等待,直到有結果返回或者超時。
有關線程池以及?Executor 框架的內容,可以閱讀這篇文章:https://blog.csdn.net/a745233700/article/details/109410376
參考文章:https://blog.csdn.net/u011480603/article/details/75332435
總結
以上是生活随笔為你收集整理的JUC多线程:创建线程的四种方式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 操作系统:经典进程同步问题 之 生产者-
- 下一篇: 《剑指offer》答案整理