java 多執行緒(關於Thread的講解)

人圭日月發表於2020-02-08


學習來源於b站 https://www.bilibili.com/video/av82219418
自己僅作 java 多執行緒的記錄,看視訊主要還是看書看不下去了…
然後推薦大家看看《JAVA併發程式設計實踐》

Thread 介紹

Thread 生命週期圖

在這裡插入圖片描述

  • java 應用程式的main函式是一個執行緒,是被jvm啟動時呼叫,執行緒的名字叫main
  • 實現一個執行緒,必須建立Thread 例項,override run方法,並且呼叫start方法
  • 在JVM啟動後,實際上有多個執行緒,但是至少有一個非守護執行緒
  • 當你呼叫一個執行緒start方法的時候,此時至少有兩個執行緒,一個是呼叫你的執行緒,還有一個執行run方法的執行緒

下面程式碼觀察,Thread 執行緒用 run 執行和 start 執行的區別

public static void main(String[] args) {

       new Thread(new Runnable() {
           @Override
           public void run() {
               System.out.println("start:"+Thread.currentThread().getName()); // 當前執行緒名
           }
       }).start();

       new Thread(new Runnable() {
           @Override
           public void run() {
               System.out.println("run:"+Thread.currentThread().getName()); // main 執行緒
           }
       }).run(); // 呼叫run方法,僅僅是例項物件呼叫
   }

Thread 構造方法介紹

  • 建立執行緒物件Thread,預設有一個執行緒名,以Thread-開頭,以0開始計數,具體實現參考以下原始碼
    public Thread() { init(null, null, "Thread-" + nextThreadNum(), 0); }
  • 如果構造執行緒物件時,未傳入ThreadGroup,Thread 會預設獲取父執行緒的ThreadGroup 作為該執行緒的ThreadGroup , 此時子執行緒和父執行緒在同一個ThreadGroup中,參考以下原始碼
    /* If the security doesn't have a strong opinion of the matter use the parent thread group. */ if (g == null) { g = parent.getThreadGroup(); }
  • 構造Thread 的時候傳入stacksize 代表著該執行緒佔用stack大小,如果沒有指定stacksize 的大小,預設是0,0代表著會忽略該引數,該引數會被JNI函式去使用。需注意:該引數有一些平臺有效,有些平臺無效。

Daemon 執行緒

守護執行緒

Thread thread = new Thread(() -> {

        Thread innerThread = new Thread(() -> {

            while(true){
                System.out.println("do something...");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        innerThread.setDaemon(true); // 設定為守護執行緒
        innerThread.start();

        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    });

    thread.start();
}

作用:當你執行緒呼叫其他執行緒的時候,你的執行緒結束時,設定為守護執行緒會隨著外面的執行緒而結束。

join 方法

等待當前執行緒執行完後,主執行緒繼續執行的意思。

public static void testJoin() throws InterruptedException {
        Thread t = new Thread(new Runnable() {
            @SneakyThrows
            @Override
            public void run() {
                Thread.sleep(10000);
            }
        });
        t.start();
        t.join();

        System.out.println("finish...");
    }

上述例子,如果不新增t.join(); 的話,裡面會把輸出語句列印出來,但是新增後,等上面的執行緒執行完畢後,在執行主執行緒。當我們統計其他執行緒的時候,就需要用到join。

執行緒結束

方式1,利用標記的方式

public class ThreadStop extends Thread {

    private volatile boolean start = true;

    @Override
    public void run() {

        while(start){
			// TODO
        }
    }

    public void shutdown(){
        this.start = false;
    }

    public static void main(String[] args) throws InterruptedException {
        ThreadStop threadStop = new ThreadStop();
        threadStop.start();
        Thread.sleep(1000);
        threadStop.shutdown();
    }
}

方式2:利用interrupt方法

public class ThreadStop extends Thread {

    @Override
    public void run() {

        while(true){
             if(Thread.interrupted()){
                break; // return
            }
        }
    }


    public static void main(String[] args) throws InterruptedException {
        ThreadStop threadStop = new ThreadStop();
        threadStop.start();
        Thread.sleep(1000);
        threadStop.interrupt();
    }
}

暴力解決執行緒

無論標記,還是打斷的方式,都需要在某一處判斷,但是多數時候,block的狀態,我們才想結束;
而jdk 提供的stop()方法已經過期了。

之前守護執行緒提起過,如果我們主執行緒被結束的話,守護程式也是被jvm關閉的。

/**
 * @Author: shengjm
 * @Date: 2020/2/5 15:48
 * @Description: 讓傳入的執行緒成為守護執行緒,這樣關閉執行緒的時候,關閉守護執行緒
 */

public class ThreadCloseForce {

    private boolean finished = false;

    private Thread executeThread;

    public void execute(Runnable task){
        executeThread = new Thread(new Runnable() {
            @Override
            public void run() {
                Thread runner = new Thread(task);
                runner.setDaemon(true);
                runner.start();
                try {
                    runner.join();
                    finished = true;
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        executeThread.start();
    }

    public void shutdown(long mills){
        long currentTime = System.currentTimeMillis();
        while(!finished){
            if(System.currentTimeMillis() - currentTime > mills){
                System.out.println("超時啦~");
                executeThread.interrupt();
                break;
            }

            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
                break;
            }
        }

        finished = false;

    }

    public static void main(String[] args) {
        ThreadCloseForce threadCloseForce = new ThreadCloseForce();
        long start = System.currentTimeMillis();
        threadCloseForce.execute(()->{
//            while(true){
//                //TODO
//            }
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        threadCloseForce.shutdown(10000);
        long end = System.currentTimeMillis();
        System.out.println("總共消耗時間"+(end-start));

    }
}

相關文章