Java多線程基礎概念篇-01

Java多線程概念基礎篇

引言

多線程是Java的一個重要主體之一,接下來將要講解Java多線程篇章,涉及到的是JUC即java.util.concurrent包,該包是由Java大師 Doug Lea完成並且於JDK1.5之後併入到java中。我很歡迎讀者在討論區留言,也很樂意爲入門的學者充當佈道人,有問題就儘管提問吧。

一、線程的基本概念

線程的狀態圖大致如下:

在這裏插入圖片描述

分爲以下5種狀態:

  1. 新建狀態(New): 線程對象被創建後,就進入了新建狀態。例如,Thread thread = new Thread()。
  2. 就緒狀態(Runnable): 也被稱爲“可執行狀態”。線程對象被創建後,其它線程調用了該對象的start()方法,從而來啓動該線程。例如,thread.start()。處於就緒狀態的線程,隨時可能被CPU調度執行。
  3. 運行狀態(Running) : 線程獲取CPU權限進行執行。需要注意的是,線程只能從就緒狀態進入到運行狀態
  4. 阻塞狀態(Blocked) : 阻塞狀態是線程因爲某種原因放棄CPU使用權,暫時停止運行。直到線程進入就緒狀態,纔有機會轉到運行狀態。阻塞的情況分三種:
    (01) 等待阻塞 – 通過調用線程的wait()方法,讓線程等待某工作的完成。
    (02) 同步阻塞 – 線程在獲取synchronized同步鎖失敗(因爲鎖被其它線程所佔用),它會進入同步阻塞狀態。
    (03) 其他阻塞 – 通過調用線程的sleep()或join()或發出了I/O請求時,線程會進入到阻塞狀態。當sleep()狀態超時、join()等待線程終止或者超時、或者I/O處理完畢時,線程重新轉入就緒狀態。
  5. 死亡狀態(Dead) : 線程執行完了或者因異常退出了run()方法,該線程結束生命週期。

以上的這5種狀態涉及到的內容包括Object類, Thread和synchronized關鍵字。這些內容我們會在後面的章節中逐個進行學習。

  • Object類,定義了wait(), notify(), notifyAll()等休眠/喚醒函數。
  • Thread類,定義了一些列的線程操作函數。例如,sleep()休眠函數, interrupt()中斷函數, getName()獲取線程名稱等。
  • synchronized,是關鍵字;它區分爲synchronized代碼塊和synchronized方法。synchronized的作用是讓線程獲取對象的同步鎖。

在後面詳細介紹wait(),notify()等方法時,我們會分析爲什麼“wait(), notify()等方法要定義在Object類,而不是Thread類中”。

二、實現多線程的方式

在Java中,實現多線程的方式總的來說分爲兩種,一種是通過繼承Thread自定義自己的線程類,另外一種是通過實現Runnable等接口。以下簡單貼個demo:

  • 通過繼承線程類:
public class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println(Math.random());
    }
    public static void main(String[] args) {
        new MyThread().start();
    }
}
  • 通過實現Runnable接口創建線程:
 public static void main(String[] args) {
        Runnable target=()->{
            System.out.println(Math.random());
        };
        new Thread(target).start();
    }
  • 通過實現Callable接口創建線程:
public static void main(String[] args) throws Exception{
        Callable<String> callable = () -> {
            System.err.println("我是返回值的run方法!");
            Thread.sleep(10000);
            return "我是回調對象";
        };
        FutureTask<String> futureTask = new FutureTask<>(callable);
        Thread thread = new Thread(futureTask);
        thread.start();
        System.out.println("***********等待************");
        while (true){
            //每1S檢測是否完成?
            Thread.sleep(1000);
            System.out.println("未完成,執行檢查...");
            if (futureTask.isDone()){
                System.out.println("**********完成************");
                System.out.println(futureTask.get());
                break;
            }
        }
        System.out.println("結束");
    }

對以上三種實現多線程方式做一個總結:

  • 繼承Thread、實現Runable、實現Callable接口,都可以創建線程,其中Callable接口需要使用FutureTask封裝。
  • 把Runable和Callable歸納爲一類,繼承Thread爲一類,個人建議用實現接口的方式,因爲可讀性好,簡便,並且複用性更強。

那麼,JAVA是怎麼實現線程的呢?接下來就以上述的demo做一個簡單的剖析。

首先,我們查看Thread線程類的源碼:

//我會列出線程類中的與上述demo相關的方法或者變量,來探究線程是怎麼運行的
public class Thread implements Runnable {
  /* 要運行的東西 */
  private Runnable target;
  //線程狀態內部枚舉類
  public enum State {
        NEW,
        RUNNABLE,
        BLOCKED,
        WAITING,
        TIMED_WAITING,
        TERMINATED;
    }
  //線程內部的run方法
    @Override
    public void run() {
        if (target != null) {
          //就是調用Runnable的run方法體。
            target.run();
        }
    }
  public synchronized void start() {
        /**
         *主方法線程或“系統”不調用此方法
         */
        if (threadStatus != 0)
            //threadStatus=0,即該線程是新創建的線程,處於NEW狀態
            throw new IllegalThreadStateException();
        /* 通知組此線程即將啓動
         *以便可以將其添加到組的線程列表中
         *該組的未啓動計數可以減少。 */
        group.add(this);
        //定義是否執行完成的局部變量started
        boolean started = false;
        try {
            //調用JVM實現的方法
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
            }
        }
    }
    private native void start0();
...
}

通過以上源碼,我們可以知道,多線程的實現縱然可以繼承Thread重寫run方法,亦或者實現Runnable接口等,實質上殊途同歸,都是封裝到當前的run方法體當中去,並且交由JVM管理。大致的流程如下:

創建線程->修改runnable成員變量||重寫run方法->執行start方法,實質交由start0,交由JVM創建線程執行。

通過如上的講解,我想讀者應該能夠比較容易理解線程的創建,當然,對於Thread類的講解,後續會有專門的篇章。在剛纔的探究中,我們也看到了多線程模型中,start()與run()是不一致的。前者是創建新線程來運行指定的run方法體,後者是當前線程直接調用run方法執行。

三、總結

  • 線程分爲5種狀態:NEW 、RUNNABLE 、RUNNING 、BLOCKED、 DEAD
  • 線程創建的方式多種多樣,但實際殊途同歸

由於本篇是基礎篇章,內容也相對簡單,暫告一段落。

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