JDK5.0之后Java多線程的實現方式變成了四種,下面來簡單的列舉一下,如果需要更深入的了解,強烈建議閱讀一下源碼。
一、繼承Thread類重寫run()方法:
1. 創建一個繼承於Thread類的子類
2. 重寫Thread類的run() --> 將此線程執行的操作聲明在run()中
3. 創建Thread類的子類的對象
4. 通過此對象調用start()
1 // 1、 創建一個繼承於Thread類的子類
2 class Test1 extends Thread {
3
4 // 2、 重寫Thread類的run()
5 @Override
6 public void run() {
7 //Thread.currentThread().getName():獲取當前線程的名字
8 System.out.println("線程需要執行的代碼" + "->"
9 + Thread.currentThread().getName());
10 }
11
12 }
13
14 public class ThreadTest1 {
15 public static void main(String[] args) {
16 // 3、 創建Thread類的子類的對象
17 Test1 test1 = new Test1();
18
19 //多線程當然可以創建多個對象來開啟多個線程
20 Test1 test2 = new Test1();
21
22 // 4、通過此對象調用start()方法啟動線程
23 //start()方法的作用:1)啟動當前線程 2)調用當前線程的run()方法
24 test1.start();
25 test2.start();
26 }
27 }
順便插一句並不是test1先調用了start()方法就一定先比test2先執行,不清楚的小伙伴建議先了解一下多線程的概念,這里主要是對實現多線程的幾種方式簡單總結,概念不再贅述。
二、實現Runnable接口:
1. 創建一個實現Runnable接口的類
2. 實現Runnable中的run()方法
3. 創建實現類的對象
4. 將此對象作為參數傳遞到Thread類的構造器中,創建Thread類的對象
5. 通過Thread類的對象調用start()
1 //1. 創建一個實現Runnable接口的類
2 class Test2 implements Runnable {
3 // 2. 實現Runnable中的run()方法
4 @Override
5 public void run() {
6 System.out.println("線程需要執行的代碼" + "->"
7 + Thread.currentThread().getName());
8 }
9 }
10
11 public class ThreadTest2 {
12 public static void main(String[] args) {
13 // 3. 創建實現類的對象
14 Test2 test = new Test2();
15 // 4. 將此對象作為參數傳遞到Thread類的構造器中,創建Thread類的對象
16 Thread t1 = new Thread(test);
17 Thread t2 = new Thread(test);
18 // 5. 通過Thread類的對象調用start()
19 t1.start();
20 t2.start();
21
22 }
23 }
這種實現的方式沒有類的單繼承性的局限性更適合處理多個線程有共享數據的情況。
三、實現Callable接口
1.創建Callable的實現類
2.實現call方法,將此線程需要執行的操作聲明在call()中
3.創建Callable接口實現類的對象
4.將此Callable接口實現類的對象作為傳遞到FutureTask構造器中,創建FutureTask的對象
5.將FutureTask的對象作為參數傳遞到Thread類的構造器中,創建Thread對象,並調用start()
6.獲取Callable中call方法的返回值
1 import java.util.concurrent.Callable;
2 import java.util.concurrent.ExecutionException;
3 import java.util.concurrent.FutureTask;
4
5 //1.創建Callable的實現類
6 class Test3 implements Callable<Object>{
7 //2.實現call方法,將此線程需要執行的操作聲明在call()中
8 @Override
9 public Object call() throws Exception {
10 int sum = 0;
11 for (int i = 1; i <= 100; i++) {
12 if(i % 2 == 0){
13 System.out.println(i);
14 sum += i;
15 }
16 }
17 return sum;
18 //如果不需要方法返回值
19 //return null;
20 }
21 }
22
23
24 public class ThreadTest3 {
25 public static void main(String[] args) {
26 //3.創建Callable接口實現類的對象
27 Test3 numThread = new Test3();
28 //4.將此Callable接口實現類的對象作為傳遞到FutureTask構造器中,創建FutureTask的對象
29 FutureTask futureTask = new FutureTask(numThread);
30 //5.將FutureTask的對象作為參數傳遞到Thread類的構造器中,創建Thread對象,並調用start()
31 new Thread(futureTask).start();//用了匿名內部類
/*可以和上面一樣寫成(相當於):
Thread thread = new Thread(futureTask);
thread.start();
*/
32
33 try {
34 //6.獲取Callable中call方法的返回值
35 //get()返回值即為FutureTask構造器參數Callable實現類重寫的call()的返回值。
36 Object sum = futureTask.get();
37 System.out.println("總和為:" + sum);
38 } catch (InterruptedException e) {
39 e.printStackTrace();
40 } catch (ExecutionException e) {
41 e.printStackTrace();
42 }
43 }
44
45 }
這種創建線程的方式更加的麻煩,但是人家相比較實現Runnable接口的方式更強大
相比實現Runnable接口的好處;
1. call()可以有返回值
2. call()可以拋出異常,被外面的操作捕獲,獲取異常的信息
3. Callable支持泛型
四、線程池
1. 提供指定線程數量的線程池
2.執行指定的線程的操作。需要提供實現Runnable接口或Callable接口實現類的對象
3.關閉連接池
1 import java.util.concurrent.ExecutorService;
2 import java.util.concurrent.Executors;
3
4 /**
5 * 這里可以用實現Runnable的方式 也可以用實現Callable的方式
6 *
7 */
8 class Test4 implements Runnable {
9 @Override
10 public void run() {
11 System.out.println("代碼");
12 }
13 }
14
15 public class ThreadTest4 {
16 public static void main(String[] args) {
17 // 1. 提供指定線程數量的線程池 這里設置為10
18 ExecutorService service = Executors.newFixedThreadPool(10);
19
20 // 2.執行指定的線程的操作。需要提供實現Runnable接口或Callable接口實現類的對象
21 service.execute(new Test4());// 適合適用於Runnable
22 // service.submit(Callable callable);//適合使用於Callable
23
24 // 3.關閉連接池
25 service.shutdown();
26 }
27
28 }
這種方式的好處:
1.提高響應速度(減少了創建新線程的時間)
2.降低資源消耗(重復利用線程池中線程,不需要每次都創建)
3.便於線程管理
以上簡單列舉了創建線程的四種方式,有不少東西沒有寫;尤其是第四種,設置線程池屬性等都沒有演示。對於還在學習基礎的小伙伴來說,前兩種需要先重點掌握,后面兩種可以先了解一下,等有一些多線程基礎之后再進行后續學習。
