Runnable接口
創建線程的另一種方法是聲明實現 Runnable 接口的類。該類實現 run 方法。然后創建Runnable的子類對象,傳入到某個線程的構造方法中,開啟線程。
實現Runnable接口的意義:Runnable接口用來指定每個線程要執行的任務。包含了一個 run 的無參數抽象方法,需要由接口實現類重寫該方法。
創建線程的步驟。
1、定義類實現Runnable接口。
2、覆蓋接口中的run方法。。
3、創建Thread類的對象
4、將Runnable接口的子類對象作為參數傳遞給Thread類的構造函數。
5、調用Thread類的start方法開啟線程。
//實現Runnable接口
public class MyRunnable implements Runnable { public void run() { for(int i=0;i<100;i++){ System.out.println(Thread.currentThread().getName()+":"+i); } } }
public static void main(String[] args) { //創建線程任務對象
MyRunnable my=new MyRunnable(); //創建線程對象
Thread t=new Thread(my); Thread t2=new Thread(my); //開啟線程
t.start(); t2.start(); }
實現Runnable的原理
實現Runnable接口,避免了繼承Thread類的單繼承局限性,可以多個繼承。覆蓋Runnable接口中的run方法,將線程任務代碼定義到run方法中。
創建Thread類的對象,只有創建Thread類的對象才可以創建線程。線程任務已被封裝到Runnable接口的run方法中,而這個run方法所屬於Runnable接口的子類對象,所以將這個子類對象作為參數傳遞給Thread的構造函數,這樣,線程對象創建時就可以明確要運行的線程的任務。
實現Runnable的好處
聲明實現Runnable接口避免了單繼承的局限性,所以較為常用。實現Runnable接口的方式,更加的符合面向對象思想,線程分為兩部分,一部分線程對象,一部分線程任務。繼承Thread類,線程對象和線程任務耦合在一起。一旦創建Thread類的子類對象,既是線程對象,又有線程任務。實現runnable接口,將線程任務單獨分離出來封裝成對象,類型就是Runnable接口類型。Runnable接口對線程對象和線程任務進行解耦。
線程的匿名內部類使用
public static void main(String[] args) { //繼承Thread類方式
//此處的new Thread()方法整體就是匿名內部類,創建線程對象,可以直接重寫run()方法
new Thread(){ public void run() { for(int i=0;i<100;i++){ System.out.println(Thread.currentThread().getName()+":"+i); } } }.start(); //實現Runnable接口方式
//此處的Runnable r=new Thread()方法整體就是匿名內部類,重寫Runnable中的run()方法
Runnable r=new Runnable(){ public void run() { for(int i=0;i<100;i++){ System.out.println(Thread.currentThread().getName()+":"+i); } } }; //創建線程對象並開啟線程
new Thread(r).start(); }
線程池:
線程池,其實就是一個容納多個線程的容器,其中的線程可以反復使用,省去了頻繁創建線程對象的操作,無需反復創建線程而消耗過多資源。
圖解示例:
線程池主要用來解決線程生命周期開銷問題和資源不足問題。通過對多個任務重復使用線程,線程創建的開銷就被分攤到了多個任務上了,而且由於在請求到達時線程已經存在,所以消除了線程創建所帶來的延遲。這樣,就可以立即為請求服務,使用應用程序響應更快。另外,通過適當的調整線程中的線程數目可以防止出現資源不足的情況。
使用線程池方式--Runnable接口
通常,線程池都是通過線程池工廠創建,再調用線程池中的方法獲取線程,再通過線程去執行任務方法。
Executors:線程池創建工廠類
ExecutorService:線程池類
Future接口:用來記錄線程任務執行完畢后產生的結果。線程池創建與使用
使用線程池中線程對象的步驟:
創建線程池對象
創建Callable接口子類對象
提交Callable接口子類對象
關閉線程池
public static void main(String[] args) { //獲取線程池對象
ExecutorService es=Executors.newFixedThreadPool(2);//此處表示線程池中有2條線程 //創建線程任務對象
MyRunnable r=new MyRunnable(); //將線程任務交給線程池執行
//此處要有3條任務執行,而線程池只有2條,所以只能先執行前2個任務,其中一個任務執行完畢再繼續第3個任務執行
es.submit(r); es.submit(r); es.submit(r); //銷毀線程池
es.shutdown(); }
使用線程池方式—Callable接口
Callable接口:與Runnable接口功能相似,用來指定線程的任務。其中的call()方法,用來返回線程任務執行完畢后的結果,call方法可拋出異常。
ExecutorService:線程池類
<T> Future<T> submit(Callable<T> task):獲取線程池中的某一個線程對象,並執行線程中的call()方法
Future接口:用來記錄線程任務執行完畢后產生的結果。線程池創建與使用
使用線程池中線程對象的步驟:
1.創建線程池對象
2.創建Callable接口子類對象
3.提交Callable接口子類對象
4.關閉線程池
public static void main(String[] args) throws InterruptedException, ExecutionException { //獲取線程池對象
ExecutorService es=Executors.newFixedThreadPool(2); //創建線程任務
MyCallable c=new MyCallable(); //將線程任務交給線程池執行
Future<String> f=es.submit(c);
//注意:submit方法調用結束后,程序並不終止,是因為線程池控制了線程的關閉。將使用完的線程又歸還到了線程池中 //獲取返回值
String str=f.get(); System.out.println(str); //銷毀線程池
es.shutdown(); }
l Callable接口實現類,call方法可拋出異常、返回線程任務執行完畢后的結果
public class GetSum implements Callable<Integer> { private int num; public GetSum() { super(); } public GetSum(int num) { super(); this.num = num; } public Integer call() throws Exception { int sum=0; for(int i=1;i<=num;i++){ sum+=i; } return sum; }
public static void main(String[] args) throws InterruptedException, ExecutionException { //異步計算1-n的和 //兩條線程分別計算1-100和1-200的和 //獲取線程池對象
ExecutorService es=Executors.newFixedThreadPool(2); //創建線程任務對象 構造方法有返回值
GetSum r1=new GetSum(100); GetSum r2=new GetSum(200); //將線程任務提交給線程池執行
Future<Integer> f1=es.submit(r1); Future<Integer> f2=es.submit(r2); //獲取返回值 任務同時進行,但結果有先后
System.out.println(f1.get()); System.out.println(f2.get()); //銷毀線程池
es.shutdown(); }
//5050 20100