Java併發基礎(上)——Thread

        併發編程可使咱們將程序劃分爲多個分離的,獨立運行的任務。經過多線程機制,這些獨立任務都將由執行線程來驅動。在使用線程時,CPU將輪流給每一個任務分配佔用時間,每一個任務都以爲本身在佔用CPU,但實際上CPU時間是劃分爲片斷分配給了全部任務。java

定義任務編程

繼承Thread類多線程

      咱們能夠繼承Thread類,並重寫run方法。併發

public class SimpleThread extends Thread {
    @Override
    public void run(){
        for(int i = 0; i < 5; i++){
            System.out.println(getName() + i);
        }
    }
    
    public static void main(String[] args) {
        SimpleThread thread = new SimpleThread();
        thread.start();
        System.out.println(Thread.currentThread().getName());
    }
}

實現Runnable接口異步

      固然,咱們還能夠實現Runnable接口,並實現run接口,而後提交給Thread實例。ide

public class Task implements Runnable{
    public void run(){
        for(int i = 0; i < 5; i++){
            System.out.println(Thread.currentThread().getName() + i);
        }
    }
    public static void main(String[] args) {
        Thread thread = new Thread(new Task());
        thread.start();
        System.out.println(Thread.currentThread().getName());
    }
}

Callable與Futurespa

       咱們知道run方法是沒有返回值的,也就意味着任務完成後沒法獲取結果,因此咱們須要Callable接口來幫助咱們返回任務結果,它和Runnable接口很類似,因此咱們也須要實現Callable接口的call方法。而Future接口則用來獲取異步計算結果的,咱們對執行結果獲取,取消,或者判斷是否完成。可是Callable接口並無繼承Runnable,因此並不能直接提交給Thread實例,因此咱們還須要FutureTask類,它同時實現了Runnable接口和Callable接口,咱們能夠用FutureTask包裝Callable對象,再提交給Thread實例。操作系統

import java.util.concurrent.*;

public class TaskWithResult implements Callable<Integer> {
    public Integer call(){
        int total = 0;
        for(int i = 0; i < 100; i++){
            total += i;
        }
        return total;
    }

    public static void main(String[] args) throws InterruptedException, ExecutionException{
        RunnableFuture<Integer> task = new FutureTask<Integer>(new TaskWithResult());
        Thread thread = new Thread(task);
        thread.start();
        while (!task.isDone()){
            System.out.println(task.get().toString());
        }
    }
}

 後臺線程線程

       後臺線程,也叫守護線程,是指程序在運行的時候在後臺提供一種通用服務的線程,而且這種線程並不屬於程序中不可或缺的部分。因此,當全部非後臺線程結束時,後臺線程也會被結束,無論後臺線程是否完成。並且,後臺線程的子線程也是後臺線程。code

public class Daemon implements Runnable {
    @Override
    public void run() {
        for(int i = 0; i < 10; i++){
            try{
                Thread.sleep(1000);
                System.out.println(i);
            }catch (InterruptedException e){
                System.out.println(e.getMessage());
            }
        }
    }

    public static void main(String[] args) throws InterruptedException{
        Thread thread = new Thread(new Daemon());
        thread.setDaemon(true);
        thread.start();
        Thread.sleep(1000 * 5);
        System.out.println(Thread.currentThread().getName());
    }
}

        這裏要注意setDaemon方法必需要在start以前調用,才能將其設置爲後臺線程。

線程的生命週期

新建(new)

       當線程被建立時,它只會短暫地處於這種狀態。此時它已經分配了必須的系統資源,並執行了初始化。此刻線程已經有資格得到CPU時間了,以後調度器將把這個線程轉變爲可運行狀態或阻塞狀態。

就緒(Runnable)

       在這種狀態下,只要調度器把時間片分配給線程,線程就能夠運行。也就是說,在任意時刻,線程能夠運行也能夠不運行。只要調度器能分配時間片給線程,它就能夠運行,這不一樣於死亡和阻塞狀態。

阻塞(Blocked)

       線程可以運行,但有某個條件阻止它的運行。當線程處於阻塞狀態時,調度器將忽略線程,不會分配給線程任何CPU時間。直到線程從新進入了就緒狀態,它纔有可能執行操做。

       一個任務進入阻塞狀態,可能有如下緣由:

       (1)經過調用sleep()使任務進入休眠狀態,在這種狀況下,任務在指定時間內不會運行。

       (2)經過調用wait()使線程掛起。直到線程獲得了notify()或notifyAll()消息,線程纔會進入就緒狀態。

       (3)任務在等待某個輸入/輸出完成。

       (4)任務試圖在某個對象上調用其同步控制方法,可是對象鎖不可用,由於另一個任務已經獲取了這個鎖。

死亡(Dead)

       處於死亡或終止狀態的線程將再也不是可調度的,而且不再會獲得CPU時間,它的任務已結束,或再也不是可運行的。任務死亡的一般方式是從run()方法返回,可是任務的線程還能夠被中斷。

線程控制

線程優先級

       線程的優先級將該線程的重要性傳遞給調度器。儘管CPU處理現有線程集的順序是不肯定的,可是調度器將傾向於讓優先級最高的線程先執行。固然,這並不意味着優先權較低的線程將得不到執行(優先級不會致使死鎖)。咱們能夠用getPriority()來讀取現有線程的優先級,經過setPriority()來修改它。儘管JDK有10個優先級,可是它與多數操做系統都不能映射的很好。因此設置優先級時,通常使用MAX_PRIORITY、NORM_PRIORITY、MIN_PRIORITY三種級別。

休眠(Sleep)

       在線程執行的過程當中調用sleep方法讓其休眠(也就是進入阻塞狀態)。sleep會拋出InterruptedException。

public class Sleep {
    public static void main(String[] args) {
        System.out.println("休眠開始");
        try{
            Thread.sleep(1000 * 2);
        }catch (InterruptedException e){
            System.out.println(e.getMessage());
        }
        System.out.println("休眠結束");
    }
}

讓步(Yield)

      若是知道已經完成了run方法的循環的一次迭代過程當中所需的工做,就能夠給線程調度機制一個暗示:你的工做已經作的差很少了,可讓別的線程使用CPU了,這個

暗示將經過調用yeild做出,固然,這只是一種建議,沒有任何機制保證它將會採納。當線程切換出去後,只有優先級與當前線程相同,或優先級比當前線程更高的處於就緒的線程纔會得到執行機會,所以徹底有可能線程轉入就緒後,調度器又將其調度出來從新執行。

Join
   join能夠一個讓線程等待另外一個線程執行完成,調用線程將被阻塞,直到被join的線程執行完成

public class Task implements Runnable{
    public void run(){
        for(int i = 0; i < 5; i++){
            System.out.println(Thread.currentThread().getName() + i);
        }
        try{
            Thread.sleep(1000);
        }catch (InterruptedException e){
            System.out.println(e.getMessage());
        }
    }
    public static void main(String[] args) throws Exception{
        Thread thread = new Thread(new Task());
        thread.start();
        thread.join();
        System.out.println(Thread.currentThread().getName());
    }
}
相關文章
相關標籤/搜索