多線程<small>建立線程的三種方式</small>
程序和線程
幾乎所有的作業系統都支援同時運作多個任務,一個任務通常就是一個程式,每個運作中的程式就是一個程序Process。當一個程式運作時,内部可能包含多個順序執行流,每個順序執行流就是一個線程Thread。
線程是程序的組成部分,一個程序可以擁有多個線程,一個線程必須擁有一個父程序。
線程可以擁有自己的堆棧,自己的程式計數器和自己的局部變量,但是線程不擁有自己的資源。它與父程序的其他線程共享該程序的所有資源。
線程的執行是搶占式的,是以任何一個線程都有可能被挂起,以便另外一個線程的執行。
線程可以建立和撤銷另外一個線程。
線程共享的環境包括:程序的代碼段,程序的共有資料等。利用這些共享的資料,線程很容易實作項目之間的通信。
1. 繼承Thread類建立線程類
- 定義Thread類的子類,并重寫run()方法,該run(0方法的方法體代表了線程需要完成的任務,是以run()方法稱為線程執行體。
- 建立了Thread類的子類,級建立了線程對象。
- 調用線程對象的start()方法來啟動該線程。
currenThread(),擷取目前線程對象
getName(),目前線程的名字,預設是main ,Thread-0,Thread-1,Thread-n
package com.manyThread;
/**
* @author futao
* Created on 18-1-8-下午8:49.
* 多線程的實作方式1,繼承Thread類,重寫run()方法
*/
public class FirstThread extends Thread {
private int i;
@Override
public void run() {
for (; i < 1000; i++) {
System.out.println("==" + this.getName() + " " + i);
}
}
public static void main(String[] args) {
for (int i = 0; i < 1000; i++) {
System.out.println("main===" + Thread.currentThread().getName() + " " + i);
if (i == 20) {
FirstThread firstThread = new FirstThread();
firstThread.setName("Niu");
firstThread.start();
new FirstThread().start();
}
}
}
}
可以發現,i的值是不連續的,是以用繼承Thread的方式實作的多線程是不能夠共享線程的執行個體變量的。
使用繼承Thread類的方法來建立線程時,多個線程之間無法共享線程的執行個體變量,因為每個線程都需要執行個體化一個對象。
2. 實作Runnable接口建立線程類
- 定義Runnable接口的實作類,并重寫該接口的run()方法,該run()方法的方法體同樣是該線程的線程執行體。
- 建立Runnable實作類的執行個體,并以此執行個體作為Thread的target來建立Thread對象,該Thread對象才是真的線程對象。
package com.manyThread;
/**
* @author futao
* Created on 18-1-8-下午9:10.
* 多線程的實作方式2,實作Runnable接口,重寫run()方法
*/
public class FirstRunnable implements Runnable {
private int i;
@Override
public void run() {
for (; i < 1000; i++) {
System.out.println(Thread.currentThread().getName() + "===" + i);
}
}
public static void main(String[] args) {
for (int j = 0; j < 100; j++) {
System.out.println("main" + j);
if (j == 20) {
FirstRunnable firstRunnable = new FirstRunnable();
/*Runnable的實作類的對象僅僅用來作為new Thread()的target*/
Thread thread = new Thread(firstRunnable, "myNewThread");
thread.start();
Thread thread1 = new Thread(firstRunnable, "2thread");
thread1.start();
}
}
}
}
變量 i 的值是連續的,是以通過實作Runnable接口的方式實作的多線程是可以共享線程類的執行個體變量的
這是因為在這種方式下,程式所建立的Runnable對象隻是線程的target,而多個線程可以共享同一個target,是以多個線程可以共享同一個線程類(實際上是線程的target類)的執行個體變量。
是以通過實作Runnable接口建立的多線程時,
Thread
類的作用就是把run()方法包裝成線程執行體。
3.使用Callable和Future建立線程
Runnable接口的增強版
- 建立Callable接口的實作類,并實作call()方法,作為線程的執行體,且該call()方法有傳回值,可以直接使用Lambda表達式建立Callable對象
- 使用FutureTask類來包裝Callable對象,該FutureTask對象封裝了該Callable對象的call()方法的傳回值。
- 使用FutureTask對象作為Thread對象的target建立并且啟動線程
- 調用FutureTask對象的get()方法來獲得子線程執行之後的傳回值。(将導緻主線程被阻塞,直到call()方法傳回傳回值)
package com.manyThread;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* @author futao
* Created on 18-1-11-上午10:21.
*/
public class FirstCallable implements Callable {
@Override
public Object call() throws Exception {
return 6666;
}
public static void main(String[] args) {
// FirstCallable firstCallable = new FirstCallable();
// FutureTask futureTask = new FutureTask(firstCallable);
// Thread thread = new Thread(futureTask);
// thread.start();
// try {
// System.out.println(futureTask.get());
// } catch (InterruptedException e) {
// e.printStackTrace();
// } catch (ExecutionException e) {
// e.printStackTrace();
// }
FutureTask futureTask1 = new FutureTask(() -> {
int i = 0;
for (; i <= 100; i++) {
System.out.println(Thread.currentThread().getName() + i);
}
return i;
});
for (int i = 0; i < 50; i++) {
System.out.println(Thread.currentThread().getName() + i);
if (i == 20) {
Thread thread = new Thread(futureTask1);
thread.start();
try {
/*get()方法将導緻主線程被阻塞,直到call()方法結束并傳回傳回值為止*/
System.out.println(futureTask1.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
}
}
4. 建立線程的三種方式比較
