Thread Runnable線程及stop()、interrupt()、sleep()等方法講解

  • 如何開啓線程

開啓線程2種方式:

自定義類繼承Thread類,重寫run方法;new 自定義類().start()

自定義類實現Runnable接口,實現run方法;new Thread(new 自定義類()).start()

  • Thread和Runable

Thread是對線程的抽象封裝,完成獨立完成某個任務的調度,包括任務的開始、暫停、結束等操作。本身是實現Runnable接口,所以具有Runnable的所有功能。

Runnable 是對任務的抽象封裝,業務任務的具體邏輯封裝。本身不具有獨立資源調度。需要Thread協助完成,這也就是前面說的開啓線程需要先new Thread() 通過參數把Runnable傳遞進去。

  • JVM開啓,默認開啓哪些線程(JDK1.8爲例)
public static void main(String[] args) {
        //虛擬機線程系統的管理接口(JVM開啓的線程);對JVM系統進行監控時,使用類ManagementFactory
        ThreadMXBean threadMXBean= ManagementFactory.getThreadMXBean();
        ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(false, false);
        for(ThreadInfo threadInfo:threadInfos){
            System.out.println("["+threadInfo.getThreadId()+"]"+threadInfo.getThreadName());
        }
        /* 一般項目啓動會啓動的線程有:
        * [8]JDWP Command Reader
        [7]JDWP Event Helper Thread
        [6]JDWP Transport Listener: dt_socket
        [5]Attach Listener
        [4]Signal Dispatcher
        [3]Finalizer  線程管理Object finalize()方法,每個對象銷燬時都會調用這個方法;不推薦在這個方法中寫資源釋放,有時調用不到(main結束這個線程也就沒有了)
        [2]Reference Handler
        [1]main 主線程,線程關閉其他線程會依次關閉
        * */
    }
  • stop/interrupt/sleep等方法總結

stop():結束線程,比較野蠻會立即結束線程。比如當前線程正在寫一個文件,或者正在寫入數據庫,正在完成一半任務時,接到stop命令,立即線程結束,導致文件不完整,錯誤的文件或數據庫數據不完整等問題。所以JDK1.8之後不在推薦使用。當然如果能夠保證當前線程沒有這些類似任務,採用stop停止線程也是可以的。

interrupt():標記線程狀態爲中斷狀態(線程本身並不會結束),此時線程中如果有join、wait、sleep等方法調用的時候會拋出異常java.lang.InterruptedException。在線程的run()方法中我們需要自己寫邏輯結束線程。isInterrupt()可獲取線程的中斷狀態。Thread.interrupt()靜態方法內部會調用當前線程的isinterrupt()方法,但是內部也會再次把狀態標誌位修改,所有一般都是用實例interrupt方法。對於Runnable實例化的線程,一般用Thread.currentThread().interrupt() 方法判斷。

sleep():使當前線程進入阻塞狀態。同時這個方法內部會監測interrupt狀態,如果當前線程中斷後,同時線程還睡眠時,會報異常java.lang.InterruptedException: sleep interrupted。sleep()並不會釋放對象鎖,只有鎖代碼塊執行完後,其他線程才能夠獲取鎖

  • 線程stop方法案例
import java.util.concurrent.TimeUnit;

/**
 * @ClassName xu01
 * @Description des
 * @Author zyk
 * @Date 2019/9/24
 * @Version 1.0
 **/
public class xu01 {
    private static class UseThread extends Thread{
        @Override
        public void run() {
            while(true) {
                System.out.println("正在寫入文件,寫入部分...");
            }
        }
    }

    public static void main(String[] args) {
        UseThread useThread=new UseThread();
        useThread.start();
        //main線程睡眠20
        try {
            TimeUnit.MILLISECONDS.sleep(20);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //main線程2秒後關閉
        // 1:stop() 是一個@Deprecated不推薦使用,過於野蠻停止線程(會出現線程寫部分文件就掛了)
        useThread.stop();

        System.out.println("main 線程結束");
    }
}
  • 線程interrupt方法案例
import java.util.concurrent.TimeUnit;

/**
 * @ClassName xu01
 * @Description des
 * @Author zyk
 * @Date 2019/9/24
 * @Version 1.0
 **/
public class xu01 {
    private static class UseThread extends Thread{
        @Override
        public void run() {
            while(true) {
                System.out.println("正在寫入文件,寫入部分...");
                //判斷線程狀態是否,是否需要結束
                //                if(isInterrupted()){
                //Thread.interrupted靜態方法也是同樣的功能,內部會獲取當前線程
                if(Thread.interrupted()){
                    System.out.println("線程狀態:"+interrupted());
                    System.out.println("判斷當前文件寫完了,然後結束線程,不會導致文件是個損壞的文件");
                    break;
                }

            }
        }
    }

    public static void main(String[] args) {
        UseThread useThread=new UseThread();
        useThread.start();
        //main線程睡眠20
        try {
            TimeUnit.MILLISECONDS.sleep(20);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //main線程2秒後關閉
        //只是標記線程標誌位爲中斷狀態,需要在run方法中寫邏輯結束線程,否則線程還是不能夠結束
        useThread.interrupt();
        System.out.println("main 線程結束");
    }
}
  • 線程sleep方法案例

在線程中添加sleep睡眠邏輯,做完一件事後睡眠一段時間,再次做事情。

import java.util.concurrent.TimeUnit;

/**
 * @ClassName xu01
 * @Description des
 * @Author zyk
 * @Date 2019/9/24
 * @Version 1.0
 **/
public class xu01 {
    private static class UseThread extends Thread{
        @Override
        public void run() {
            while(true) {
                System.out.println("正在做某件任務...");
                System.out.println("結束一段任務後,線程睡眠一段時間");

                try {
                    TimeUnit.MILLISECONDS.sleep(20);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                if(Thread.interrupted()){
                    System.out.println("線程狀態:"+interrupted());
                    System.out.println("判斷當前文件寫完了,然後結束線程,不會導致文件是個損壞的文件");
                    break;
                }

            }
        }
    }

    public static void main(String[] args) {
        UseThread useThread=new UseThread();
        useThread.start();
        //main線程睡眠20
        try {
            TimeUnit.MILLISECONDS.sleep(20);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //main線程2秒後關閉
        //只是標記線程標誌位爲中斷狀態,需要在run方法中寫邏輯結束線程,否則線程還是不能夠結束
        useThread.interrupt();
        System.out.println("main 線程結束");
    }
}

注意:這樣線程並不會結束,運行結果如下

正在做某件任務...
結束一段任務後,線程睡眠一段時間
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at java.lang.Thread.sleep(Thread.java:340)
	at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
	at controller.multithread.xu01$UseThread.run(xu01.java:21)
main 線程結束
正在做某件任務...
結束一段任務後,線程睡眠一段時間
正在做某件任務...
結束一段任務後,線程睡眠一段時間

這是因爲 TimeUnit.MILLISECONDS.sleep(20);會拋出異常後,把當前線程中斷狀態又改回去了,導致線程還會繼續執行。

需要修改代碼:第一種方式,在sleep方法上添加try-catch,在InterruptedException異常代碼塊中再次調用interrupte()方法。第二種方式,把while死循環監視起來,出現InterruptedException 異常後結束線程,修改後代碼如下:

import java.util.concurrent.TimeUnit;

/**
 * @ClassName xu01
 * @Description des
 * @Author zyk
 * @Date 2019/9/24
 * @Version 1.0
 **/
public class xu01 {
    private static class UseThread extends Thread{
        @Override
        public void run() {
            try {
                while (true) {
                    System.out.println("正在做某件任務...");
                    System.out.println("結束一段任務後,線程睡眠一段時間");

                    try {
                        TimeUnit.MILLISECONDS.sleep(20);
                        //java.lang.InterruptedException: sleep interrupted 需要把這個異常拋出去
                    } catch (InterruptedException e) {
                        //interrupt();
                        throw e;
                    }

                    if (Thread.interrupted()) {
                        System.out.println("線程狀態:" + interrupted());
                        System.out.println("判斷當前文件寫完了,然後結束線程,不會導致文件是個損壞的文件");
                        break;
                    }

                }
            }catch (InterruptedException e){
                e.printStackTrace();
                System.out.println("子線程結束");
            }
        }
    }

    public static void main(String[] args) {
        UseThread useThread=new UseThread();
        useThread.start();
        //main線程睡眠20
        try {
            TimeUnit.MILLISECONDS.sleep(20);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //main線程2秒後關閉
        //只是標記線程標誌位爲中斷狀態,需要在run方法中寫邏輯結束線程,否則線程還是不能夠結束
        useThread.interrupt();
        System.out.println("main 線程結束");
    }
}
  • 線程池使用

JDK1.8不在推薦使用Executors創建線程了,推薦使用ThreadPoolExecutor,可控制最大線程數量。而Executors 最多創建線程數量是 Integer.MAX_VALUE 會導致系統OOM問題

public static void main(String[] args) throws ExecutionException, InterruptedException {
        int count=230;
        //線程池1:
        ExecutorService pool1=Executors.newCachedThreadPool();
        ExecutorService pool2=Executors.newFixedThreadPool(5);

        //線程池2:
        ThreadFactory namedThreadFactory=new ThreadFactoryBuilder()
                .setNameFormat("jake-pool-%d")
                .build();
        ExecutorService pool=new ThreadPoolExecutor(5,200,
                60L,TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>(1024),
                namedThreadFactory,new ThreadPoolExecutor.AbortPolicy());
        for(int i=0;i<count;i++){
            pool.execute(()->{
                System.out.println("========線程名稱:"+Thread.currentThread().getName());
            });
        }
        pool.shutdown();
        System.out.println("shutdown");

    }

 

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