1. 程式人生 > >Java中停止執行緒、中斷執行緒、等待狀態(wait)(基礎解析)

Java中停止執行緒、中斷執行緒、等待狀態(wait)(基礎解析)

停止執行緒

測試interrupt中斷執行緒

    測試interrupt中斷執行緒

    public class Demo {
        public static void main(String[] args) {
            StopRunnable stopRunnable = new StopRunnable();
            Thread t1 = new Thread(stopRunnable);
            t1.start();
            // 給t1執行緒有執行時間
            try {
                Thread.sleep(3000
); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } t1.interrupt(); System.out.println("執行緒已中斷"); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block
e.printStackTrace(); } System.out.println("主執行緒結束"); } } class StopRunnable implements Runnable { @Override public void run() { // 利用死迴圈 測試能不能停止執行緒 while(true) { try { // 沉睡1s
Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } // 休眠1s(寫個死迴圈 讓迴圈執行1s結束) long time = System.currentTimeMillis(); while (System.currentTimeMillis() - time < 1000) { } System.out.println(Thread.currentThread().getName() + "...run"); } } }
    測試結果: 不能結束子執行緒
    實際上這個Interrupt() 方法
    設定了 isInterrupted 布林值

    如果執行緒中有wait() 等待 或者 sleep 休眠
    這時 呼叫 interrupt方法
    會丟擲一個異常 InterruptedException
    並且清除中斷狀態

    如果執行緒中沒有等待 或者 休眠
    這時呼叫interrupt方法
    會設定中斷狀態(true/false的改變)

執行緒中沒有等待或睡眠 呼叫interrupt方法中斷執行緒

    public class Demo {
        public static void main(String[] args) {
            StopRunnable stopRunnable = new StopRunnable();
            Thread t1 = new Thread(stopRunnable);
            t1.start();
            // 給t1執行緒有執行時間
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            t1.interrupt();
            System.out.println("執行緒已中斷");

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println("主執行緒結束");
        }
    }

    class StopRunnable implements Runnable {
        @Override
        public void run() {
            // 利用死迴圈 測試能不能停止執行緒
            while(!Thread.currentThread().isInterrupted) {

                // 休眠1s(寫個死迴圈 讓迴圈執行1s結束)
                long time = System.currentTimeMillis();
                while (System.currentTimeMillis() - time < 1000) {

                }
                System.out.println(Thread.currentThread().getName() + "...run");
            }
        }
    }

利用標記 停止執行緒

    利用標記 停止執行緒

    public class Demo {
        public static void main(String[] args) {
            StopRunnable stopRunnable = new StopRunnable();
            Thread t1 = new Thread(stopRunnable);
            t1.start();
            // 給t1執行緒有執行時間
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            // 利用標記 停止執行緒
            stopRunnable.isOver = true;
            System.out.println("執行緒已中斷");

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println("主執行緒結束");
        }
    }

    class StopRunnable implements Runnable {
        public boolean isOver = false;

        @Override
        public void run() {
            // 利用死迴圈方式 測試能不能停止執行緒
            while(!isOver) {
                try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            // 休眠1s(寫個死迴圈  讓迴圈執行1s結束)
            long time = System.currentTimeMillis();
            while (System.currentTimeMillis() - time < 1000) {
            }
            System.out.println(Thread.currentThread().getName() + "...run");
            }
        }
    }

測試中斷狀態 wait方法

    public class Demo {
        public static void main(String[] args) {
            InterruptRunnable it = new InterruptRunnable();
            Thread t1 = new Thread(it);
            Thread t2 = new Thread(it);

            t1.start();
            t2.start();

            for (int i = 0; i < 50; i++) {
                if (i == 25) {
                    // 呼叫中斷方法 來清楚狀態
                    t1.interrupt();
                    t2.interrupt();
                    break;
                }
                System.out.println(i + "---");
            }
            System.out.println("主執行緒結束");
        }
    }

    class InterruptRunnable implements Runnable {
        public synchronized void run() {
            while(true) {
                try {
                    // 執行緒1進來 帶著鎖 遇到了wait方法 等待
                    //放棄了CPU的執行權 但是 鎖 會還回去
                    // 執行緒2也帶著鎖進來 又遇到wait方法 也等待
                    // 相當於兩個執行緒都在這裡等待
                    // 進入冷凍(中斷)狀態
                    // 解決冷凍(中斷)狀態
                    // 呼叫interrupt方法 清除該狀態
                    wait();
                    }catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "...run");
            }
        }
    }
    這裡利用interrupt方法 清除冷凍(中斷)狀態 
    不會中斷執行緒會強行讓執行緒繼續執行
    所以報異常後還會列印一次

利用標記法

      public class Demo {
        public static void main(String[] args) {
            InterruptRunnable it = new InterruptRunnable();
            Thread t1 = new Thread(it);
            Thread t2 = new Thread(it);

            t1.start();
            t2.start();

            for (int i = 0; i < 50; i++) {
                if (i == 25) {
                    // 使用標記
                    it.isOver = true;
                    break;
                }
                System.out.println(i + "---");
            }
            System.out.println("主執行緒結束");
        }
    }

    class InterruptRunnable implements Runnable {
        public boolean isOver = fales;
        @Override
        public synchronized void run(){
            while(!isOver){
                try{
                    wait();
                }catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "...run");
            }
        }
    }
    在使用標記法發現當到25的時候 程式不再列印 
    也不會像呼叫interrupt方法一樣報異常
    但是也和呼叫interrupt方法一樣 沒有停止執行緒
    說明使用了wait方法 會讓 執行緒等待 放棄了CPU的執行資源
    執行緒就會一直在這裡等待
    如果想喚醒執行緒 我們需要呼叫 notify方法來喚醒執行緒
    wait方法 是 Object類的

    報的異常為:IllegalMonitorStateException
    這個異常解釋中的物件監視器 就是 物件鎖
    注意: wait方法 必須用鎖物件去呼叫

注意

    interrupt方法 進來不要使用
    如果要停止執行緒 直接使用標記法停止
    只有遇到了 等待狀態 時 可以使用 該方法
    強行清除該狀態

小練習

    Person類 姓名 性別
    開啟兩個執行緒 
    一個對Person物件進行賦值
    一個對Person物件進行列印
    要求 一次列印 小明 男
         一次列印 xiaoming nv
         間隔輸出


    public class Demo {
        public static void main(String[] args) {

        }
    }

    // Person類 姓名 性別
    class Person {
        public String name;
        public String gender;
        //宣告標記 來切換 列印 和 賦值
    }

    // 賦值執行緒
    class SetRunnable implements Runnable {
        // 利用成員變數 操作同一個物件
        private Person p;
        // 定義一個標識 通過改變標識來進行間隔賦值
        Private boolean isTrue = true;

        // 利用構造方法來賦值
        public SetRunnable() {

        }
        public SetRunnable(Person p){
            this.p = p;
        }
        @Override
        public void run() {
            while(true) {
                //多個執行緒 操作共享資料
                // 新增同步鎖 解決資料安全問題
                // 為了保證兩個同步鎖的鎖物件相同 使用傳進來的p的物件
                // 賦值執行緒 需要賦值完畢 列印執行緒才能去列印
                // 列印執行緒 列印完畢 才能讓賦值執行緒 去賦值

                // 賦值的時候 列印執行緒 在等待 等賦值完成了 才能去列印
                // 列印完成後 通知複製執行緒 讓複製執行緒繼續賦值

                //wait 和 notify 使用一個標記來進行切換
                // 這個標記要給賦值執行緒使用 也要給列印執行緒使用
                // 必須保證使用的是同一個標記 標記宣告在Person類中

                synchronized(p) {
                    // 先不進入等待 要先進行賦值
                    if(p.flag == true) {
                        // 進行等待
                        try{
                            p.wait();
                        }catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                    // 間隔賦值
                    if(isTrue) {
                        p.name = "小明";
                        p.gender = "男";
                    }else{
                        p.name = "xiaoming";
                        p.gender = "nb";
                    }
                    // 改變識別符號
                    isTrue = !isTrue;
                    // 修改標記
                    p.flag = true;
                    // 喚醒執行緒
                    p.notify();
                }
            }
        }
    }

    // 列印執行緒
    class PrintRunnable implements Runnable {
        // 利用成員變數 操作同一個物件
        private Person p;

        //利用構造方法 來賦值
        public PrintRunnable() {

        }
        public PrintRunnable(Person p) {
            this.p = p
        }
    }

    @Override
    public void run(){
        while(true){
            synchronized(p) {
                if(p.flag == false) {
                    // 進入等待
                    try{
                        p.wait();
                    }catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
                // 列印名字 和 性別
                System.out.println(p.name + "..." + p.gender);
                // 修改標記
                p.flag = false;
                // 喚醒執行緒
                p.notify();
            }
        }
    }