Java多線程 -- 01 線程的創(chuàng)建與啟動

1.多線程的概述
(1)線程、進(jìn)程、程序的區(qū)別與聯(lián)系?(來源,《瘋狂Java講義》P716)

程序:只是一個靜態(tài)的指令集合。
進(jìn)程(Process):是一個正在系統(tǒng)中活動的指令集合(即進(jìn)程是處于運行過程中的程序,并且具有一定的獨立功能)進(jìn)程是系統(tǒng)進(jìn)行資源分配和調(diào)度的一個獨立單位。進(jìn)程具有:獨立性動態(tài)性并發(fā)性
線程:是進(jìn)程的組成部分,是線程的執(zhí)行單元,是獨立運行的。一個進(jìn)程可以擁有多個線程,同理,一個線程必須有一個父進(jìn)程

聯(lián)系與區(qū)別:
進(jìn)程中加入了時間的概念,進(jìn)程具有自己的生命周期和各種不同的狀態(tài),這些概念在 程序中都是不具備的。線程擁有自己的堆棧、自己的程序計數(shù)器、自己的局部變量,但不擁有系統(tǒng)資源(因為這些資源是共享資源,是要與父進(jìn)程中其他線程公用的)。

一個線程可以創(chuàng)建和撤銷另一個線程,同一個進(jìn)程中的多個線程之間可以并發(fā)執(zhí)行。

總結(jié):
1.一個程序運行后至少有一個進(jìn)程,一個進(jìn)程里至少要包含一個線程(可以包含多個線程)。
2.操作系統(tǒng)可以同時執(zhí)行多個任務(wù),每個任務(wù)就是進(jìn)程;每個進(jìn)程可以同時執(zhí)行多個更小的任務(wù),這每個更小的任務(wù)就是線程。

現(xiàn)在的操作系統(tǒng)都支持多進(jìn)程的并發(fā),但在具體的實現(xiàn)細(xì)節(jié)上可能因為硬件和操作系統(tǒng)的不同而采用不同的策略,常用的方式有:共用式的多任務(wù)操作策略(效率不高),搶占式多任務(wù)操作策略(效率高,如UNIX/LINUX、Windows2000等系統(tǒng))

(2)多線程的優(yōu)勢

當(dāng)操作系統(tǒng)創(chuàng)建一個進(jìn)程時,須為該進(jìn)程分配獨立的內(nèi)存空間,并分配大量的相關(guān)資源;但創(chuàng)建一個線程則簡單的多,因此使用多線程來實現(xiàn)并發(fā)比使用多進(jìn)程來實現(xiàn)并發(fā)的性能要高的多

使用多線程的優(yōu)點:
1.進(jìn)程之間不能共享內(nèi)存,但線程之間共享內(nèi)存很容易
2.系統(tǒng)創(chuàng)建一個進(jìn)程時,需要為該進(jìn)程分配系統(tǒng)資源,但創(chuàng)建一個線程則付出的代價小的多,即使用多線程來實現(xiàn)多任務(wù)并發(fā)比多進(jìn)程的效率高
3.Java語言內(nèi)置了多線程功能支持,而不是單純地作為底層操作系統(tǒng)的調(diào)度方式,從而簡化了Java多線程的編程

2.線程的創(chuàng)建與啟動

Java使用Thread類代表線程,所有的線程對象都必須是Thread類或其子類的實例

創(chuàng)建線程的三種方式:

繼承Thread類
實現(xiàn)Runnable接口
實現(xiàn)Callable接口

(1)方式1:繼承Thread類
實現(xiàn)步驟:
1.定義Thread子類,并重寫Thread類的run()方法,該方法體就是縣城執(zhí)行體
2.創(chuàng)建Thread類的實例,即創(chuàng)建線程對象(注意此時還沒有啟動)
3.調(diào)用線程對象的start()方法來啟動該線程

這三個步驟缺一不可

public class ThreadTest extends Thread{
    
    public void run() {
        System.out.println(this.getName());
        System.out.println("run方法");
    }
    
    public static  void main(String[] args) {
        ThreadTest tt = new ThreadTest();//創(chuàng)建線程
        tt.start(); //啟動線程
        System.out.println(Thread.currentThread().getName());
        System.out.println("main方法");       
    }
}

注意:
1.如果在啟動線程處采用tt.run()的形式,則系統(tǒng)會將該線程對象tt當(dāng)成普通對象,從而將run()方法當(dāng)成普通方法,即不會啟動新的線程
2.上面代碼實際會產(chǎn)生兩個線程,一個是顯示創(chuàng)建,一個是主線程(main()的方法體就是主線程的執(zhí)行體)
3.使用繼承Thread類的方法來創(chuàng)建線程類時,多個線程之間是無法共享線程類的類實例變量

Thread中的重要方法:
Thread(); //創(chuàng)建Thread的對象
Thread(Runnable target); //根據(jù)傳入的Runnable接口的實現(xiàn)類實例來創(chuàng)建Thread接口
Thread(Runnable target, String name); //在創(chuàng)建線程對象時為其指定名字
static Thread currentThread(); //返回當(dāng)前正在執(zhí)行的線程對象
String getName(); //返回調(diào)用該方法的線程的名字
void setName(String name); //為調(diào)用該方法的線程重新設(shè)置線程名字

(2)方式2:實現(xiàn)Runnable接口
步驟:
1.定義實現(xiàn)Runnable接口的類,并重寫Runnable接口的run()方法,即線程執(zhí)行體
2.創(chuàng)建Runnable實現(xiàn)類的實例(注意,這并不是線程對象)。并以此實例作為Thread的target來創(chuàng)建Thread對象。此時該Thread對象才是真正的線程對象
3.調(diào)用線程對象的start()方法來啟動該線程

public class RunnableTest implements Runnable{
    public void run() {
        System.out.println(Thread.currentThread().getName());
        System.out.println("run方法");
    }
    
    public static  void main(String[] args) {
        RunnableTest rt = new RunnableTest();// 1
        Thread tt = new Thread(rt);// 2
        tt.start();// 3
        System.out.println(Thread.currentThread().getName());
        System.out.println("main方法");       
    }
}

注意:在這種方式下,創(chuàng)建的多個線程是可以共享線程類的實例變量。因為,這里創(chuàng)建的Runnable實現(xiàn)類的實例只是作為線程的target,而多線程是可以共享同一個target,這樣多個線程就可以共享同一個線程類

(3)方式3:使用Callable和Future創(chuàng)建線程
通過實現(xiàn)Runnable接口來創(chuàng)建線程的本質(zhì)是:通過Thread類來講Runnable類的run()方法包裝成了線程執(zhí)行體。但是請注意,不可以直接將其他的任意方法包裝成線程執(zhí)行體。(C#中可以)

Callable接口提供了call()方法可以作為線程執(zhí)行體,但是該方法有返回值,且可以聲明拋出異常

由于Callable并不是Runnable接口的子接口,因此不能直接作為Thread的target使用,

public interface Callable<V> {
    V call() throws Exception;
} 

Java5提供了一個Future接口,可以用來封裝call()方法的返回值,

public interface Future<V> {
    boolean cancel(boolean mayInterruptIfRunning); // 試圖取消該Future里關(guān)聯(lián)的Callable任務(wù)
    boolean isCancelled();//如果在Callale任務(wù)正常完成前被取消,則返回true
    boolean isDone();//如果Callable任務(wù)以完成則返回true
    V get() throws InterruptedException, ExecutionException;//返回call()方法的返回值,調(diào)用該方法將導(dǎo)致程序阻塞,必須等待子線程結(jié)束后才會得到返回值
    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;//在規(guī)定的timeout和unit時間內(nèi)阻塞,超時后會拋出TimeOutException
}

該接口有個實現(xiàn)類,F(xiàn)utureTask,實現(xiàn)了Runnable接口,因此可以將這個返回值作為Thread的target,從而就將Callable的call()方法與Thread關(guān)聯(lián)起來了。

public class FutureTask<V> implements RunnableFuture<V> {
    
    public FutureTask(Callable<V> callable) {...}
}

創(chuàng)建步驟:
1.創(chuàng)建Callable:創(chuàng)建Callable接口的實現(xiàn)類,并實現(xiàn)call()方法,作為線程執(zhí)行體。并創(chuàng)建該實現(xiàn)類的實例
2.封裝Callable:使用FutureTask類來包裝Callable對象的call()方法返回值,F(xiàn)utureTask(Callable<V> callable)
3.創(chuàng)建線程并啟動:使用FutureTask對象作為Thread對象的target創(chuàng)建線程對象,并啟動。
4.獲取返回值:調(diào)用FutureTask對象的get()方法來獲得子線程執(zhí)行體結(jié)束后的返回值

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;


public class CallableTest{  
    public static  void main(String[] args) throws Exception{
        MyCallable mc = new MyCallable(); //1
        FutureTask<Integer> ft = new FutureTask<Integer>(mc); //2
        Thread t = new Thread(ft); //3
        for(int i = 0; i < 20; i++) {
            if(i == 4) {
                t.start(); //3
            }
            System.out.println(Thread.currentThread().getName());
            System.out.println("main方法");   
        }
        System.out.println(ft.get());//4
    }
}

class MyCallable implements Callable<Integer>{
    public Integer call() {
        System.out.println("call方法");
        Integer i = new Integer(12); 
        return i;
    }
}
3.創(chuàng)建線程的三種方式對比

把實現(xiàn)Runnable和Callable接口的方式可以當(dāng)成一種方式,只是Callable接口里的call()方法可以有返回值和拋出異常

實現(xiàn)Runnable和Callable的方法的優(yōu)點:
1.線程類只是實現(xiàn)了該接口,還可以繼承其他類(因為類是單繼承,接口是可以多實現(xiàn),多繼承的)
2.多線程可以共享同一個target對象,適合處理同一份資源的情況。
3.缺點:代碼復(fù)雜

采用繼承Thread類的方式:
1.代碼簡單,訪問當(dāng)前線程無序Thread.currentThread(),直接用this調(diào)方法即可
2.不能再繼承其他的類

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容