天天看點

啟動線程的正确和錯誤方式&正确停止線程

作者:蠟筆小賢

一:啟動線程的正确和錯誤方式

1.start()方法的執行流程

- 檢查線程狀态(隻有線程為0(new)狀态時才能啟動線程,否則會抛異常,運作中或已經結束的狀态均會抛異常)

- 加入線程組

- 調用start0()方法啟動線程

#注意:start()方法是synchronized修飾的,是以該方法是線程安全的

package threadcoreknowledge.startthread;

/**
 * @author 小賢
 * @PackageName:
 * @ClassName:StartAndRunMethod
 * @Description:
 * @date 2022/1/3 22:20
 */
public class StartAndRunMethod {
    public static void main(String[] args) {
        Runnable runnable = () -> {
            System.out.println(Thread.currentThread().getName());
        };
        runnable.run();//隻是單純的調用run方法
        new Thread(runnable).start();//建立一個線程,經曆線程的各個生命周期
    }
}
           

二:正确停止線程

1.原理:使用interrupt來通知,而不是強制

#Java沒有提供任何機制來安全地終止線程。但它提供了中斷( Interruption),這是一種協作機制

#使用interrupt來通知線程停止的原因是:【通知線程停止的通知方】是不了解【被要求停止線程】的内部機制,而【被要求停止線程】方是知道自己停止線程時所需要執行的業務,是以最終停止線程的決定權還是在【被要求停止線程】方,我們能做的就是通知他要停止線程。

2.正确停止線程的正确姿勢

`(1)子方法将中斷信号抛出,讓被請求停止線程方處理`

package threadcoreknowledge.stopthreads;

/**
 * @author 小賢
 * @PackageName:
 * @ClassName:RightWayStopThreadInProd
 * @Description:
 * @date 2022/1/12 22:58
 */
public class RightWayStopThreadInProd implements Runnable {
    @Override
    public void run() {
        while (true) {
            System.out.println("線程開始執行任務");
            try {
                innerMethod();
            } catch (InterruptedException e) {
                e.printStackTrace();
                System.out.println("哎呀,發生錯誤了,趕緊記錄一下日志。");
                break;
            }
        }
    }

    private void innerMethod() throws InterruptedException {
        Thread.sleep(2000);
    }

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

`(2)子方法将中斷信号捕獲,捕獲處理完畢後将線程狀态再次設定為中斷狀态`

package threadcoreknowledge.stopthreads;

/**
 * @author 小賢
 * @PackageName:
 * @ClassName:RightWayStopThreadInProd
 * @Description:
 * @date 2022/1/12 22:58
 */
public class RightWayStopThreadInProd2 implements Runnable {
    @Override
    public void run() {
        while (true) {
            if (Thread.currentThread().isInterrupted()) {
                System.out.println("線程被中斷,任務執行完畢");
                break;
            }
            System.out.println("線程開始執行任務");
            innerMethod();

        }
    }

    private void innerMethod() {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
            Thread.currentThread().interrupt();
        }
    }

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

(3)volatile并不是一個正确的停止線程的方案,因為如果一個線程長期處于阻塞的狀态,volatile并不能讓線程響應中斷信号

package threadcoreknowledge.stopthreads.volatiledemo;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.BlockingQueue;

/**
 * @author 小賢
 * @PackageName:
 * @ClassName:VolatileCantStopThread
 * @Description:
 * @date 2022/1/15 20:23
 */
public class VolatileCantStopThread {

    /* * @Author 小賢
     * @Description //生産者
     * @Date 20:27 2022/1/15
     * @Param
     * @return
     **/
    class producer implements Runnable{

        BlockingQueue blockingQueue;

        public volatile boolean result = false;

        public producer(BlockingQueue blockingQueue) {
            this.blockingQueue = blockingQueue;
        }

        @Override
        public void run() {
            try {
                int num = 0;
                while (num <= 100000 && !result) {
                    if (num % 100 == 0) {
                        System.out.println(num + "進入隊列");
                        blockingQueue.put(num);
                        Thread.sleep(10);
                    }
                    num++;
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                System.out.println("生産者線程結束");
            }
        }
    }

    /* * @Author 小賢
     * @Description //消費者
     * @Date 20:30 2022/1/15
     * @Param
     * @return
     **/
    class consumer {

        BlockingQueue blockingQueue;

        public consumer(BlockingQueue blockingQueue) {
            this.blockingQueue = blockingQueue;
        }

        public boolean isTakeOver() {
            if (Math.random() > 0.95) {
                return false;
            }
            return true;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ArrayBlockingQueue queue = new ArrayBlockingQueue(10);
        VolatileCantStopThread volatileCantStopThread = new VolatileCantStopThread();
        producer producer = volatileCantStopThread.new producer(queue);
        Thread thread = new Thread(producer);
        thread.start();
        Thread.sleep(1000);
        consumer consumer = volatileCantStopThread.new consumer(queue);
        while (consumer.isTakeOver()) {
            System.out.println(queue.take() + "被消費了");
            Thread.sleep(10);
        }
        System.out.println("消費者消費完畢");
        producer.result = true;

    }
}
           

3.java異常體系

- Throwable:是java異常體系的根類,他的兩個子孩子為:Error和Exception

- Error:通常是指程式出現了無法解決的錯誤,程式奔潰情況,如記憶體溢出等問題

- Exception:這個是我們日常用得比較多的,他分為RuntimeException(運作時異常),還有其他一些checkException(可預見異常)

#unCheckException(不可預見異常):Error和RuntimeException

#checkException:除unCheckException以外的異常

啟動線程的正确和錯誤方式&amp;正确停止線程

如何正确停止線程

  1. 原理:用interrupt來請求線程停止而不是強制,好處是安全。因為被停止的線程可能需要處理一些資料再結束線程,貿然停掉線程可能會造成程式出錯。
  2. 想停止線程,要請求方、被停止方、子方法被調用方互相配合才行: a) 作為被停止方:每次循環中或者适時檢查中斷信号,并且在可能抛出InterrupedException的地方處理該中斷信号; b) 請求方:發出中斷信号; c) 子方法調用方(被線程調用的方法的作者)要注意:優先在方法層面抛出InterrupedException,或者檢查到中斷信号時,再次設定中斷狀态;
  3. stop/suspend已廢棄,volatile的boolean無法處理長時間阻塞的情況

怎樣處理不可響應中斷的阻塞

沒有特定的方法,需要具體情況分析,用特定的方法喚醒線程。