併發編程之——線程基礎

一、線程和進程

 

進程:程序運行資源分配的最小單位,進程內部有多個線程,會共享這個進程的資源。

線程:CPU調度的最小單位,必須依賴進程而存在,線程之間共享進程的資源。

 

二、線程的開啓方式

1.繼承Thread

public class UserThread extends Thread {

    @Override
    public void run() {
        System.out.println("通過繼承Thread類開啓新線程!");
    }
}

2.實現Runnable接口

public class UseRunnable implements Runnable {

    @Override
    public void run() {
        System.out.println("通過實現Runnable接口開啓新線程!");
    }
}

3.實現Callable接口

public class UseCallable implements Callable<String> {

    @Override
    public String call() throws Exception {
        System.out.println("通過實現Callable接口開啓新線程!");
        return "通過實現Callable接口開啓新線程!";
    }
}

測試代碼:

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //1.使用繼承Thread的方式開啓新線程
        Thread thread = new UserThread();
        thread.start();
        //2.使用實現Runnable接口的方式開啓新線程
        Thread runnable = new Thread(new UseRunnable());
        runnable.start();
        //3.使用實現Callable接口的方式開啓新線程
        //包裝Callable爲Runnable的類,因爲Thread不接受直接傳遞Callable,FutureTask也爲Runnable的子類
        //FutureTask會在Runnable的run方法中調用Callable的call方法,從而達到開啓新線程的效果。
        FutureTask<String> futureTask = new FutureTask<>(new UseCallable());
        Thread callable = new Thread(futureTask);
        callable.start();
        //通過FutureTask的get方法拿到返回值,如果Callable沒執行完沒獲取到返回值,會阻塞。
        String result = futureTask.get();
        System.out.println(result);
    }

注意:使用實現Callable接口的方式,需要使用FutureTask包裝Callable,再傳入至Thread中。

 

三、線程有關的API

 

Thread方法:

           start():開啓並運行一個新的線程。

           getId():返回該線程的id

           getName():返回該線程的名字

           getPriority():返回該線程的優先級

           getState():返回該線程的狀態

           isAlive():檢測該線程是否還活着

           isDaemon():檢測該線程是否爲守護線程

           join():在A線程中調用B線程的join()方法,則爲A線程等待B線程執行完後再執行,即A線程阻塞,直到B線程執行完。

          Thread.yield():靜態方法,使當前線程放棄cpu的執行權重新進入就緒狀態,注意該方法持有的鎖是不釋放的。

          Thread.sleep(long millis):靜態方法,使當前線程睡眠,單位毫秒,注意該方法持有的鎖是不釋放的。

           interrupt():將該線程中斷(實際並不會中斷,只是將中斷標誌設置爲true),如果線程正處在sleep(),join(),wait()方法中時(也就是阻塞時)調用該方法,該方法會拋出異常。

           isInterrupted():檢測該線程是否已經中斷,返回true或false。

           Thread.interrupted():靜態方法,檢測當前線程是否已經中斷,返回true或false。

           Thread.currentThread():返回對當前正在運行的線程。

                          

Object方法:

           wait():使當前持有鎖的線程進入等待狀態,注意該方法會釋放持有的鎖。

           wait(long):使當前持有鎖的線程進入等待狀態,並設置超時時間,超時後會自動喚醒,注意該方法會釋放持有的鎖。

           notify():隨機喚醒一個通過該對象加鎖並且處於等待中的線程。

           notifyAll():喚醒所有通過該對象加鎖並且等待中的線程。

 

四、線程的生命週期

 

1.新建:就是new出來的線程

2.就緒:調用線程的start()方法後,這時線程處於等待CPU分配資源階段,誰分配到CPU資源誰先開始執行。

3.運行:就緒線程被調度並獲得CPU資源時,則進入運行狀態,運行run方法中的代碼。

4.阻塞:在運行狀態的時候,可能因爲某些原因導致運行狀態的線程變成了阻塞狀態,比如調用了該線程的sleep()、wait()或調用其它線程的join(),使當前線程進入了阻塞狀態,這個時候需要其他機制將處於阻塞狀態的線程喚醒,比如調用notify()、notifyAll()方法或等待。注意,喚醒的線程不會立刻執行run(),它們要再次等待CPU分配資源後再次進入運行狀態。

5.銷燬:如果線程正常執行完畢或拋出異常導致結束,那麼線程就要被銷燬,釋放資源。

 

 

五、synchronized內置鎖

synchronized爲對象鎖,鎖的是對象的實例,常說的類鎖其實也是對象鎖,鎖的是類的class對象,每個類的class對象在虛擬機中只有一個,所以類鎖也只有一個。

在靜態方法定義中加鎖,則是爲對該類的class對象加鎖,在普通方法定義中加鎖,則是爲當前this對象進行加鎖。

 

六、ThreadLocal

線程變量,可以理解爲是個map,類型 Map<Thread,Object>(實際上Key是ThreadLocal,是對Thread的包裝),適合存儲小對象,不會有線程安全問題,ThreadLocal的作用是爲了能夠在當前線程中有屬於自己的變量。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章