一、多線程是什麼?爲什麼要用多線程?
介紹多線程之前要介紹線程,介紹線程則離不開進程。
首先 進程 :是一個正在執行中的程序,每一個進程執行都有一個執行順序,該順序是一個執行路徑,或者叫一個控制單元;
線程:就是進程中的一個獨立控制單元,線程在控制着進程的執行。一個進程中至少有一個進程。
多線程:一個進程中不只有一個線程。
爲什麼要用多線程:
①、爲了更好的利用cpu的資源,如果只有一個線程,則第二個任務必須等到第一個任務結束後才能進行,
如果使用多線程則在主線程執行任務的同時可以執行其他任務,而不需要等待;
②、進程之間不能共享數據,線程可以;
③、系統創建進程需要爲該進程重新分配系統資源,創建線程代價比較小;
④、Java語言內置了多線程功能支持,簡化了java多線程編程。
二、線程的生命週期:
整理:start(新建)----->就緒(等待jvm的線程調度器)----->run(運行)------>終止
運行期間三種狀態: 就緒、阻塞及死亡
新建 :從新建一個線程對象到程序start() 這個線程之間的狀態,都是新建狀態;
就緒 :線程對象調用start()方法後,就處於就緒狀態,等到JVM裏的線程調度器的調度;
運行 :就緒狀態下的線程在獲取CPU資源後就可以執行run(),此時的線程便處於運行狀態,
運行狀態的線程可變爲就緒、阻塞及死亡三種狀態。
等待/阻塞/睡眠 :在一個線程執行了sleep(睡眠)、suspend(掛起)等方法後會失去所佔有的資源,從而進入阻塞狀態,
在睡眠結束後可重新進入就緒狀態。
終止 :run()方法完成後或發生其他終止條件時就會切換到終止狀態。
三.三種創建線程方法
三種方法對比:
繼承Thread:線程代碼存放在Thread子類run方法中。
優勢:編寫簡單,可直接用this.getname()獲取當前線程,不必使用Thread.currentThread()方法。
劣勢:已經繼承了Thread類,無法再繼承其他類。
實現Runnable:線程代碼存放在接口的子類的run方法中。
優勢:避免了單繼承的侷限性、多個線程可以共享一個target對象,非常適合多線程處理同一份資源的情形。
劣勢:比較複雜、訪問線程必須使用Thread.currentThread()方法、無返回值。
實現Callable:
優勢:有返回值、避免了單繼承的侷限性、多個線程可以共享一個target對象,非常適合多線程處理同一份資 源的情形。
劣勢:比較複雜、訪問線程必須使用Thread.currentThread()方法
建議使用實現接口的方式創建多線程。
代碼如下:
package thread.basic;
/**
* @Package:thread.basic
* @Class:Thread
* @Description: TODO
* @Author:何仲奇
* @Date:Created in 2019-03-13 23:14
* @Company:
* @Version:
* @Modified By:
*/
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* 創建線程三種方式:
* 1.繼承Thread類
* 2.實現Runnable接口
* 3.通過Callable和Future創建線程
*/
public class ThreadMethod {
public static void main(String[] args) {
//第一種:繼承Thread類
ThreadDemo td = new ThreadDemo("td");
ThreadDemo ts = new ThreadDemo("ts");
td.start();
ts.start();
//主線程
for (int i = 0; i < 5; i++) {
System.out.println("main" + ":run" + i);
}
//第二種:實現Runnable接口
RunnableDemo runnableDemo = new RunnableDemo();
//不要顯式創建線程,請使用線程池。
//線程資源必須通過線程池提供,不允許在應用中自行顯式創建線程。
//說明:使用線程池的好處是減少在創建和銷燬線程上所花的時間以及系統資源的開銷,解決資源不足的問題。
// 如果不使用線程池,有可能造成系統創建大量同類線程而導致消耗完內存或者“過度切換”的問題
Thread t1 = new Thread(runnableDemo);
Thread t2 = new Thread(runnableDemo);
//開啓線程並調用run方法。
t1.start();
t2.start();
//第二種優化:後續補充
//第三種方法:通過Callable和Future創建線程
//1.創建對象
CallableFutrueDemo call = new CallableFutrueDemo();
//2.創建FutureTask,並啓動線程(使用FutureTask包裝CallableTest對象)
FutureTask<String> ft = new FutureTask<String>(call);
for (int i = 0; i <= 2; i++) {
//輸出主線程
System.out.println(Thread.currentThread().getName() + "主線程的i爲:" + i);
if(2 == i) {
//不要顯式創建線程,請使用線程池。
Thread t = new Thread(ft,"子線程");
t.start();
}
}
//獲取並輸出子線程call()方法的返回值
try {
//get()方法獲取返回結果
System.out.println("子線程的返回值爲" + ft.get());
}catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
/**
*1.繼承Thread類
* 步驟:
* ①、定義類繼承Thread;
* ②、複寫Thread類中的run方法;
* 目的:將自定義代碼存儲在run方法,讓線程運行
* ③、調用線程的start方法:
* 該方法有兩步:啓動線程,調用run方法。
*/
class ThreadDemo extends Thread {
/**
* 構造方法,設置線程名
* @param name
* */
public ThreadDemo(String name) {
super(name);
}
@Override
public void run(){
for (int i = 0; i < 5; i++) {
//currentThread() 獲取當前線程對象(靜態)。
// getName() 獲取線程名稱。
System.out.println("第一種:"+"線程名:"+this.getName() + ";線程對象:" +this.currentThread());
}
}
}
/**
* 2、實現Runnable接口: 接口應該由那些打算通過某一線程執行其實例的類來實現。類必須定義一個稱爲run 的無參方法。
*
* 實現步驟: ①、定義類實現Runnable接口
*
* ②、覆蓋Runnable接口中的run方法
*
* 將線程要運行的代碼放在該run方法中。
*
* ③、通過Thread類建立線程對象。
*
* ④、將Runnable接口的子類對象作爲實際參數傳遞給Thread類的構造函數。
*
* 自定義的run方法所屬的對象是Runnable接口的子類對象。所以要讓線程執行指定對象的run方法就要先明確run方法所屬對象
*
* ⑤、調用Thread類的start方法開啓線程並調用Runnable接口子類的run方法。
*/
class RunnableDemo implements Runnable{
private int tick = 10;
/**
* 覆蓋Runnable接口中的run方法,並將線程要運行的代碼放在該run方法中。
*/
@Override
public void run() {
while (true) {
if(tick > 0){
//Thread.currentThread().getName()
System.out.println("第二種" +Thread.currentThread().getName() + "..." + tick--);
}
}
}
}
/**
* 3、通過Callable和Future創建線程:
*
* 實現步驟:①、創建Callable接口的實現類,並實現call()方法,該方法將作爲線程執行體,且具有返回值。
*
* ②、創建Callable實現類的實例,使用FutrueTask類進行包裝Callable對象,
* FutureTask對象封裝了Callable對象的call()方法的返回值
*
* ③、使用FutureTask對象作爲Thread對象啓動新線程。
*
* ④、調用FutureTask對象的get()方法獲取子線程執行結束後的返回值。
*/
class CallableFutrueDemo implements Callable<String> {
/**複寫call() 方法,call()方法具有返回值
* @return
* @throws Exception
*/
@Override
public String call() throws Exception {
System.out.println("第三種:" + Thread.currentThread().getName() + "的變量值爲:");
return Thread.currentThread().getName();
}
}