1. 什么是並發與並行
要想學習多線程,必須先理解什么是並發與並行
並行:指兩個或多個事件在同一時刻發生(同時發生)。
並發:指兩個或多個事件在同一個時間段內發生。
2. 什么是進程、線程
進程:
進程是正在運行的程序的實例。
進程是線程的容器,即一個進程中可以開啟多個線程。
比如打開一個瀏覽器、打開一個word等操作,都會創建進程。
線程:
線程是進程內部的一個獨立執行單元;
一個進程可以同時並發運行多個線程;
比如進程可以理解為醫院,線程是掛號、就診、繳費、拿葯等業務活動
多線程:多個線程並發執行。
3. 線程創建
Java中線程有四種創建方式:
l 繼承Thread類
l 實現Runnable接口
l 實現Callable接口
l 線程池
3.1. 繼承Thread類
3.1.1. 第一步:創建自定義線程類
package com.creatThera; import java.util.Date; /** * @Auther: lanhaifeng * @Date: 2019/11/20 0020 09:20 * @Description: 繼承Thread類實現多線程 * @statement: */ public class MyThread extends Thread{ public void run() { for (int i = 0; i<10; i++){ System.out.println("mythread線程正在執行:"+i); } } public static void main(String[] args) { MyThread myThread=new MyThread(); myThread.start(); for (int i = 0; i<10; i++){ System.out.println("主線程正在執行:"+i); } } }
執行效果如下:

3.2. 實現Runnable接口
3.2.1. 第一步:創建自定義類實現Runnable接口
package com.creatThera; /** * @Auther: lanhaifeng * @Date: 2019/11/20 0020 09:32 * @Description:實現runnable接口創建線程 * @statement: */ public class MyRunable implements Runnable { public void run() { for (int i = 0; i<10; i++){ System.out.println("myRunnable線程正在執行:"+i); } } //測試線程 public static void main(String[] args) { Thread thread=new Thread(new MyRunable()); thread.start(); for (int i = 0; i<10; i++){ System.out.println("主線程正在執行:"+i); } } }
執行效果如下:

3.3. 實現Callable接口
3.3.1. FutureTask介紹
Callable需要使用FutureTask類幫助執行,FutureTask類結構如下:

Future接口:
判斷任務是否完成:isDone()
能夠中斷任務:cancel()
能夠獲取任務執行結果:get()
3.3.2. 第一步:創建自定義類實現Callable接口
package com.creatThera; import java.util.concurrent.*; /** * @Auther: lanhaifeng * @Date: 2019/11/20 0020 09:41 * @Description: 實現callable接口創建線程 * @statement: callable接口是有返回值的 */ public class MyCallnable implements Callable<String> { public String call() throws Exception { for (int i = 0; i < 10; i++) { System.out.println("myCallable線程正在執行:"+i); } return "MyCallabe線程執行完畢"; } //測試 public static void main(String[] args) { //創建futuretask對象 FutureTask<String> futureTask = new FutureTask<String>(new MyCallnable()); //創建Thread對象,傳入futureTask Thread thread=new Thread(futureTask); thread.start(); for (int i = 0; i<10; i++){ System.out.println("主線程正在執行:"+i); } } }
運行效果圖如下:

3.4. 線程池-Executor
3.4.1. 線程池線類關系圖

Executor接口:
聲明了execute(Runnable runnable)方法,執行任務代碼
ExecutorService接口:
繼承Executor接口,聲明方法:submit、invokeAll、invokeAny以及shutDown等
AbstractExecutorService抽象類:
實現ExecutorService接口,基本實現ExecutorService中聲明的所有方法
ScheduledExecutorService接口:
繼承ExecutorService接口,聲明定時執行任務方法
ThreadPoolExecutor類:
繼承類AbstractExecutorService,實現execute、submit、shutdown、shutdownNow方法
ScheduledThreadPoolExecutor類:
繼承ThreadPoolExecutor類,實現ScheduledExecutorService接口並實現其中的方法
Executors類:
提供快速創建線程池的方法
3.4.2. 第一步:創建自定義類實現Runnable接口
package com.creatThera; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * @Auther: lanhaifeng * @Date: 2019/11/20 0020 09:52 * @Description:使用線程池創建線程 * @statement: */ public class MyExecutors { //實現runnable接口,線程池只支持runnable和callable class MyRunnable implements Runnable{ public void run() { for (int i = 0; i<10; i++){ System.out.println("myRunnable線程正在執行:"+i); } } } //測試用線程池創建線程 public static void main(String[] args) { //1.使用Executors創建線程池 ExecutorService executorService = Executors.newFixedThreadPool(10); //2.通過線程池執行線程,線程池只支持runnable和callable executorService.execute(new MyRunable()); //3.主線程循環打印 for (int i=0; i<10; i++){ System.out.println("main主線程正在執行:"+i); } } }
實現效果如下:
3.5. 小結
3.5.1. 實現接口和繼承Thread類比較
l 接口更適合多個相同的程序代碼的線程去共享同一個資源。
l 接口可以避免java中的單繼承的局限性。
l 接口代碼可以被多個線程共享,代碼和線程獨立。
l 線程池只能放入實現Runable或Callable接口的線程,不能直接放入繼承Thread的類。
擴充:
在java中,每次程序運行至少啟動2個線程。一個是main線程,一個是垃圾收集線程。
3.5.2. Runnable和Callable接口比較
相同點:
l 兩者都是接口;
l 兩者都可用來編寫多線程程序;
l 兩者都需要調用Thread.start()啟動線程;
不同點:
l 實現Callable接口的線程能返回執行結果;而實現Runnable接口的線程不能返回結果;
l Callable接口的call()方法允許拋出異常;而Runnable接口的run()方法的不允許拋異常;
l 實現Callable接口的線程可以調用Future.cancel取消執行 ,而實現Runnable接口的線程不能
注意點:
Callable接口支持返回執行結果,此時需要調用FutureTask.get()方法實現,此方法會阻塞主線程直到獲取‘將來’結果;當不調用此方法時,主線程不會阻塞!
