线程的3种创建方式
所有的線程對象都是Thread類或其子類的實例。
1.通過繼承Thread類創建線程類
1.步驟
2.代碼:
public class FirstThread extends Thread{ private int i;//重寫run方法,public void run(){for(;i<1000 ;i++){System.out.println(getName()+" "+i);//之所以可以直接調用Therad類的getName()方法,是因為該類繼承了Thread類}}public static void main(String[] args) {for(int i=0;i<100;i++){//獲取當前線程:這里是主線程 System.out.println("當前線程:"+Thread.currentThread().getName()+" "+i);if(i == 20){//啟動第一個線程new FirstThread().start();//啟動第二個線程new FirstThread().start();}}} }部分結果:
Thread-1 731 Thread-0 16 Thread-1 732 Thread-0 17 Thread-1 733 Thread-0 18 Thread-1 734 Thread-0 193.分析結果
從結果可以看出,第一個線程與第二個線程在交替運行。而且,我們可以發現,線程1的變量從i從731到734連續,而線程2從16到19連續。這說明,雖然 i 是FirstThread類的實例變量而非局部變量,但因為程序每次創建線程時,都會創建一個對象(new FirstThread),所以線程1與線程2不會共享 i 這個實例變量。
所以,通過繼承Thread類創建線程類時,多個線程之間無法共享該線程類的實例變量。
2.實現Runnable接口方式創建線程類
1.步驟
2.代碼
public class SecondThread implements Runnable {private int i;@Overridepublic void run() {for(;i<1000;i++){System.out.println(Thread.currentThread().getName()+" "+i);//實現Runnable接口時,只能使用Thread類調用當前線程}}public static void main(String[] args) {SecondThread st = new SecondThread();//創建線程的targetfor(int i=0;i<100;i++){if(i==20){new Thread(st,"線程1").start();new Thread(st, "線程2").start();//兩種方式}}}}結果片段1:
線程1 0 線程2 0 線程2 1 線程2 2 線程2 3 線程2 5 線程2 6結果片段2:
線程2 306 線程2 307 線程1 4 線程1 308 線程1 309分析結果
我們會發現,結果1片段中 i 變量沒有4,而是出現在了片段2中,而且線程2當中沒有i=4這個變量,則線程1就會有,即兩個線程共享 i 這個變量。
所以,程序創建的SecondThread對象只是Thread類構造Thread(Runnable target, String name)中的target,該target可被多個線程共享。
3.使用Callable和Future創建線程
Callabled接口有點兒像是Runnable接口的增強版,它以call()方法作為線程執行體,call()方法比run()方法功能更強大。
call()方法可以有返回值,可以聲明拋出異常類。
獲取call()方法里的返回值: 通過FutureTask類(實現Future接口)的實例對象的get()方法得到,得到結果類型與創建TutureTask類給的泛型一致。
1. 步驟
1、 定義實現Callable接口的實現類,并實現call()方法。注意:Callable有泛型限制,與返回值類型一致。這里是Integer
public class ThirdThread implements Callable<Integer>{//重寫call()方法}2、 再創建Callable實現類的實例tt。
ThirdThread tt = new ThirdThread();3、 使用FutureTask類包裝Callable的實例tt。
FutureTask<Integer> task = new FutureTask<Integer>(tt);//注意:泛型限制與返回結果一致。4、以FutureTask對象(task)作為Thread的target來創建線程,并啟動。
new Thread(task, "線程").start();5、調用FutureTask對象(task)的get()方法獲得返回值
Integer result = task.get();//會有異常2.代碼
public class ThirdThread implements Callable<Integer>{private int i;@Overridepublic Integer call() throws Exception {for(;i<100;i++){System.out.println(Thread.currentThread().getName()+" "+i);}return i;}public static void main(String[] args) {//創建Callable對象ThirdThread tt = new ThirdThread();FutureTask<Integer> task = new FutureTask<Integer>(tt);for(int i=0;i<1000;i++){System.out.println(Thread.currentThread().getName()+" "+i);if(i == 20){//創建線程 new Thread(task, "線程").start();}}try {//獲取線程返回值Integer result = task.get();System.out.println(result);} catch (InterruptedException e) { e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}}}3.結果分析
結果是,只能new一個Thread,即單線程,所以這么寫于多線程是行不通的。需要創建一個能執行多個任務的服務。
4.Future接口控制Callable里任務的幾個方法
5.Callable接口方式的多線程示例
代碼:
public class CallableAndFuture { public static class MyCallableClass implements Callable { private int i = 0; public Integer call() throws Exception { for(;i<1000;i++){System.out.println(Thread.currentThread().getName()+" "+i);}return i; } } public static void main(String[] args) { // 定義3個Callable類型的任務 MyCallableClass task1 = new MyCallableClass(); MyCallableClass task2 = new MyCallableClass(); MyCallableClass task3 = new MyCallableClass(); // 創建一個執行任務的服務 ExecutorService es = Executors.newFixedThreadPool(3); try { // 提交并執行任務,任務啟動時返回了一個Future對象, // 如果想得到任務執行的結果或者是異??蓪@個Future對象進行操作 Future future1 = es.submit(task1); // 如果調用get方法,當前線程會等待任務執行完畢后才往下執行 // System.out.println("task1: " + future1.get()); Future future2 = es.submit(task2); //System.out.println("task2 cancel: " + future2.cancel(true)); // 獲取第三個任務的輸出,因為執行第三個任務會引起異常 // 所以下面的語句將引起異常的拋出 Future future3 = es.submit(task3); //System.out.println("task3: " + future3.get()); } catch (Exception e) { System.out.println(e.toString()); } // 停止任務執行服務 es.shutdownNow(); } }結果片段:
pool-1-thread-2 502 pool-1-thread-3 867 pool-1-thread-2 503 pool-1-thread-3 868 pool-1-thread-2 504 pool-1-thread-3 869 pool-1-thread-2 505 pool-1-thread-3 870 pool-1-thread-2 506 pool-1-thread-3 871 pool-1-thread-2 507結果分析:
只要不調用get()方法,就不會阻塞,多個線程之間會交替執行;從序號可看出,每個線程的都是連續的,所以每個線程之間不共享實例變量 i ,這跟Thread方式是一樣的。原因是每個線程我們都new了一個task的。
4.三種創建方式區別
首先三種方式都可以創建多線程。
Thread方式和Callable方式不能共享實例變量;而Runnalbe方式可共享,因為能共享target。
因為通過實現Runnable接口與Callable接口類似,只是Callable接口方式的call()方法有返回值和可聲明拋出異常,所以我們將這兩種方式統稱為RC方式。通過繼承Thread類方式稱為T方式。
1. RC方式優缺點:
編程稍稍復雜些。
2. T方式優缺點:
優點:
編程簡單。
缺點:
因為單繼承的限制,不能再繼承其他類了。
綜上分析:最好采用RC方式。
總結
- 上一篇: 线程简介
- 下一篇: String直接赋字符串和new Str