
一、創建線程和使用線程方式
1.1 繼承Thread
讓線程類繼承自Thread類,然后重寫run(),把需要多線程調用的代碼放到run()中。但是需要開啟線程的時候不能調用run方法,而是需要調用start()方法。
/*** 本類用于演示第一種實現多線程的方式*/
class MyThread extends Thread{// 需要我們使用多進程運行的代碼就寫在run方法里@Overridepublic void run() { for (int i = 1; i < 5; i++) { System.out.println(Thread.currentThread().getName() + "線程正在運行~~~~~~" + i);}}
}
public class ThreadDemo01 extends Thread{public static void main(String[] args) {MyThread thread = new MyThread();thread.start();for (int i = 1; i < 5; i++) { System.out.println(Thread.currentThread().getName() + "線程正在運行======" + i);}}
}
運行結果:(兩個線程同時運行,爭搶cpu,每次執行結果不固定)

12.2 實現Runnable接口
需要我們創建的線程類實現Runnable接口。實現run方法,把需要多線程調用的代碼寫在run方法中。由于Runnable接口中只有一個run方法,所以不能直接調用start()。這個時候需要創建一個Thread類的對象來幫助我們開啟線程。創建這個Thread類對象的時候需要將我們的線程類作為參數傳遞給Thread對象。
/*** 本類用于演示第二種實現多線程的方式*/
class MyThread2 implements Runnable{// 需要我們使用多進程運行的代碼就寫在run方法里@Overridepublic void run() { for (int i = 1; i < 5; i++) { System.out.println(Thread.currentThread().getName() + "線程正在運行~~~~~~" + i);}}
}public class ThreadDemo02 {public static void main(String[] args) { MyThread2 myThread2 = new MyThread2();// myThread2.run();// 由于Runable接口只有一個run方法,所以實現Runnable接口的這種方式,線程本身沒有start方法。// 但是必須要調用start方法才能開啟多線程,所以需要創建Thread類的對象,把我們的線程類作為參數傳遞給構造方法。Thread thread = new Thread(myThread2);thread.start();for (int i = 1; i < 5; i++) { System.out.println(Thread.currentThread().getName() + "線程正在運行~~~~~~" + i);} }
}
運行結果:(兩個線程同時運行,爭搶cpu,每次執行結果不固定)

2.3 實現Callable接口
采用實現Callable接口實現多線程啟動方式以上兩種的方式不太一樣,下面就看一下怎樣啟動采用實現Callable接口的線程。
第一步:定義一個類實現Callable接口,并且指定call()返回值的類型。將業務邏輯寫在call()方法中。
第二步:創建FutureTask對象,并將Callable對象傳入FutureTask的構造方法中。
第三步:實例化Thread對象,并在構造方法中傳入FurureTask對象。
第四步:啟動線程
/*** 本類用于演示第三種實現多線程的方式*/
class MyCallable implements Callable<String>{@Overridepublic String call() throws Exception {for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().getName() + "線程正在運行~~~~~" + i);}return "MyCallable接口執行完成!";} }
public class ThreadDemo03 {public static void main(String[] args) throws Exception {// 1、創建FutureTask實例,創建MyCallable實例FutureTask<String> futureTask = new FutureTask<>(new MyCallable());// 2、創建Thread實例,來執行futureTaskThread thread = new Thread(futureTask);thread.start();// 3、主線程for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().getName() + "線程正在運行~~~~~" + i);}// 4、獲取執行結果System.out.println(futureTask.get());}
}
運行結果:

1.4 線程池
在一個應用程序中,我們需要多次使用線程,也就意味著,我們需要多次創建并銷毀線程。而創建并銷毀線程的過程勢必會消耗內存。而在Java中,內存資源是及其寶貴的,所以,我們就提出了線程池的概念。
線程池:Java中開辟出了一種管理線程的概念,這個概念叫做線程池,從概念以及應用場景中,我們可以看出,線程池的好處,就是可以方便的管理線程,也可以減少內存的消耗。
那么,我們應該如何創建一個線程池那?Java中已經提供了創建線程池的一個類:Executor而我們創建時,一般使用它的子類:ThreadPoolExecutor。
第一步:使用Executors獲取線程池對象;
第二步:通過線程池獲取對象獲取線程并執行Runable子類實例;
class MyThread4 implements Runnable{// 需要我們使用多進程運行的代碼就寫在run方法里@Overridepublic void run() { for (int i = 1; i < 5; i++) { System.out.println(Thread.currentThread().getName() + "線程正在運行~~~~~~" + i);}}
}
public class ThreadDemo04 {public static void main(String[] args) { // 第一步:使用Executors獲取線程池對象;ExecutorService executorService = Executors.newFixedThreadPool(10);// 第二步:通過線程池獲取對象獲取線程并執行Runable實例;executorService.execute(new MyThread4()); for (int i = 1; i < 5; i++) { System.out.println(Thread.currentThread().getName() + "線程正在運行~~~~~~" + i);} }
}
運行結果:

二、總結
2.1 實現接口和繼承Thread類比較
(1)接口更適合多個相同的程序代碼的線程去共享同一個資源。
實現Runnable接口這種方式,可以把多線程之間相同的操作放到一個Runnable接口的run方法中實現,然后可以通過多個thread來執行同一個Runnable實例。可以達到在一個地方開發,多個地方使用,做到代碼的復用。
(2)接口可以避免Java中單繼承的局限性。
Java的設計是單繼承的設計,如果采用繼承Thread的方式實現多線程,則不能繼承其他的類,就會錯失繼承很多父類方法。
(3)接口代碼可以被多個線程共享,代碼和線程獨立。
(4)線程池只能放入實現Runnable或Callable接口的線程,不能直接放入繼承Thread的類。
2.2 Runnable和Callable接口比較
A、相同點:
(1)兩者都是接口;
(2)都可以用來編寫多線程程序;
(3)兩者都需要調用Thread.start()啟動線程;
B、不同點:
(1)實現Callable接口的線程能返回執行結果;而實現Runnable接口的線程不能返回結果;若需要用這個線程執行任務,不需要控制則建議使用Runnable,若需要動態監控任務則建議使用Callable。
(2)Callable接口的call()方法允許拋出異常;而Runnable接口的run()方法不允許拋異常。
(3)實現Callable接口的線程可以調用Future.cancel取消執行,而實現Runnable接口的線程不能。
注意點:Callable接口支持返回執行結果,此刻需要調用FutureTask.get()方法實現,此方法會阻塞主線程直到獲取“將來”結果;不調用此方法時,主線程不會阻塞。只能在不需要并發的地方調用get()方法。