1. 線程與進程之間的區別與聯繫
在操作系統的資源管理中,某個時間內只能有一個進程佔用時間,
所謂線程就是在進程的基礎上進一步的劃分得來的,一個進程可以有多個線程,
線程是比進程更快的處理單元,而且佔用的資源比較少。
多線程的啓動,並不是按照順訊一個一個的執行,而是誰能夠佔有資源誰就先執行。
個人理解看來,一個進程就像是一個圖片下載器,
在網速足夠的情況下,如果這個軟件只有一個線程,
那麼在一個時間內,只能通過這個線程下載一幅圖像,
而如果這個下載器是多線程下載器,那麼在同一時間內,
就可以通過多個線程下載多幅圖像。但同時資源佔有率也比較高。
總的來說,線程的實現離不開進程,就比如這個軟件,如果軟件關閉了,那麼進程也就沒了。
2. 多線程的實現方式一共有三種:
第一種是繼承 Thread 類,Thread 是一個多線程的功能類,所有繼承 Thread 的類都是多線程類。
第二種是實現 Runnable 接口。
第三種是從java 1.5 以後增加的實現 Callable 接口。
這兩種接口的區別在於,Runnable 沒有返回值,但是Callable 接口具有返回值。
(1)類繼承 Thread :
class MyMessage extends Thread {}
MyMessage 由於繼承了 Thread,所以這就是一個多線程類。
每一個程序的的起點都是main方法,但是每一個線程也有起點,
這個起點就是 run() 方法,每一個繼承了 Thread 的多線程子類都需要覆寫 run() 方法。
Thread 類中的 run 方法: public void run(){};
類繼承 Thread 多線程舉例:
class ThreadTest extends Thread{ 線程主類, 通過繼承 Thread 實現 多線程
private String name;
public ThreadTest(String name) {
this.name = name;
}
@Override
public void run() { 覆寫 run() 方法
for(int i = 0; i < 100; i++) {
System.out.println(this.name + "-->" + i);
}
}
}
public class ThreadDemo { 主類
public static void main(String[] args) {
ThreadTest thread1 = new ThreadTest("繼承Thread多線程1");
ThreadTest thread2 = new ThreadTest("繼承Thread多線程2");
ThreadTest thread3 = new ThreadTest("繼承Thread多線程3");
thread1.start();
thread2.start();
thread3.start();
通過調用 run(); 也可以啓動程勳,但是都是順序知執行, 不會出現交替執行的現象。
所以只能通過調用 Thread 類中的 start(); 方法來啓動多線程。
通過start(); 方法 可以啓動多線程,每一個線程對象交替執行, 使用start();
進行啓動多線程,因爲其不僅僅是啓動線程,還會根據不同的操作系統進行資源的分配。
調用 Thread 父類中的 start(); 啓動多線程 , 但是啓動的內容爲 run(); 的方法體
}
}
之所以調用 start() 可以實現多線程的原因在於,
start() 方法中有一個 JNI (java Native Interface) 方法
這個方法可以實現對特定的操作系統進行資源的分配。
但是由於是對特定的系統,所以在一定程度上喪失了可移植性。
所以 調用 start() 方法,不僅僅是像 run() 方法一樣只是執行了多線程代碼
還實現了對不同操作系統對線程進行資源的分配。
(2) 用 Runnable 實現多線程
雖然採用繼承 Thread 類的方法可以實現多線程
但是卻又一個缺點,由於 java 的單繼承模式,如果這個類
繼承了 Thread 類,那麼久無法再繼承別的類了,所以爲了避免
這個問題的出現,也可以用實現 Runnable 接口的方法來實現多線程。
實現 Runnable 接口多線程舉例:
class ThreadImpel implements Runnable {
線程主類, 通過 實現 Runnable 接口 實現多線程,
並且也需要實現 Runnable 實現 run()方法,但是並沒有 start() 方法
private String name;
public ThreadImpel(String name) {
this.name = name;
}
public String getname() {
return this.name;
}
@Override
public void run() { // 覆寫 run() 方法
for(int i = 0; i < 100; i++) {
System.out.println(this.name + "-->" + i);
}
}
}
實際上,Thread 類中的 run() 方法也是實現了 Runnable 的接口。
但是雖然實現 Runnable 接口實現了多線程,但是這個接口中由於缺少了
start() 方法,所以不能啓動多線程。
但是在 Thread 的類中,存在一個構造方法:
public Thread (Runnable Target) {};
這個構造方法可以接受一個 Runnable 接口實現類的對象,並返回一個 Thread 對象
所以在 main 方法中可以這麼寫:
public class ThreadDemo { 主類
public static void main(String[] args) {
ThreadImpel thread1 = new ThreadImpel("實現接口Runnable多線程1");
ThreadImpel thread2 = new ThreadImpel("實現接口Runnable多線程2");
ThreadImpel thread3 = new ThreadImpel("實現接口Runnable多線程3");
new Thread(thread1).start();
new Thread(thread2).start();
new Thread(thread3).start();
或者寫成:
Thread th1 = new Thread(thread1);
Thread th2 = new Thread(thread1);
Thread th3 = new Thread(thread1);
th1.start(); 啓動多線程
th2.start();
th3.start();
}
}
實現 Runnable 接口與繼承 Thread 的區別, 由於 java 是單繼承模式,所以實現 Runnable 接口解決了這個侷限性。
Thread 類的定義: public class Thread extens Object implements Runnable
從定義上可以看出,實際上Thread類中的 run() 方法也是實現與 Runnable 接口。
還有第三種實現多線程的方法就是 類實現 Callable 接口,這個接口個 Runnable 接口的不同之處就是在於,Callable接口可以有返回值。返回值爲泛型,在實現接口的實現定義返回值類型。
(3)
通過實現 Callable 接口實現多線程
但是類聲明完成之後由於不是 Thread 的子類,並且
Thread 類的構造方法中也不含有接收 Callable 接口對象的方法
所以這個時候要依靠
類:FutureTask
public class FutureTask<V> implements RunnableFuture<V> {
public FutureTask(Callable<V> callable){ 構造方法
}
}
接口:RunnableFuture
public interface RunnableFuture<V> extends Runnable, Future<V> {}
由這個接口和類可以看出,FutureTask 的構造方法接收 Callable 接口對象,又
由於這個類實現了 RunnableFuture 這個接口。而這個接口又繼承 Runnable 接口。
所以就可以使用 Thread 中的構造方法來實現多線程的啓動。
多線程實現類:
class ThreadCall implements Callable <String > {
private int ticket = 20;
@Override
public String call() throws Exception {
for(int i = 0; i < 100; i++) {
if(this.ticket > 0) {
System.out.println("還剩票數:" + this.ticket--);
}
}
return "票賣光了!";
}
}
main方法:
public class ThreadDemo {
public static void main(String[] args) {
ThreadCall thcal1 = new ThreadCall();
ThreadCall thcal2 = new ThreadCall();
FutureTask<String> furtask1 = new FutureTask<String>(thcal1);
FutureTask<String> furtask2 = new FutureTask<String>(thcal2);
new Thread(furtask1).start();
new Thread(furtask2).start();
try {
System.out.println("線程1的返回結果:" + furtask1.get());
System.out.println("線程2的返回結果:" + furtask2.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
多線程結束之後,各個線程的返回值通過 FutureTask 的父類 Future 中的 get() 方法來獲得返回值。