Java多線程之中斷線程、中斷狀態以及間隔輸出

停止線程

停止線程:只要線程停了就叫做停止線程

使用boolean標記方法中斷線程:
        class StopRunnable implements Runnable{
            public boolean isOver = false;
            @Override
            public void run() {
                // 利用死循環方式 測試能不能停止線程
                while (!isOver) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName());
                }
            }   
        }
        public class Demo01 {
            public static void main(String[] args) throws InterruptedException {
                StopRunnable sr = new StopRunnable();
                Thread t1 = new Thread(sr);
                t1.start();
                // 給t1線程有執行的時間
                Thread.sleep(3000);
                // 利用標記停止線程
                sr.isOver = true;
                System.out.println("停止線程");
                Thread.sleep(1000);
                System.out.println("主線程結束");
            }
        }
測試interrupt中斷線程:
public class Demo {
    public static void main(String[] args) throws InterruptedException {
        StopRunnable sr = new StopRunnable();
        Thread t1 = new Thread(sr);
        t1.start();
        // 給t1線程有執行的時間
        Thread.sleep(3000);
        t1.interrupt();
        System.out.println("停止線程");
        Thread.sleep(1000);
        System.out.println("主線程結束");
    }
}

class StopRunnable implements Runnable{

    @Override
    public void run() {
        // 利用死循環方式 測試能不能停止線程
        while (!Thread.currentThread().isInterrupted()) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 休眠一秒鐘(寫個死循環 讓循環執行一秒結束)
//          long time = System.currentTimeMillis();
//          while (System.currentTimeMillis() - time < 1000) {}
            System.out.println(Thread.currentThread().getName());
        }
    }   
}

測試結果:
實際上interrupt()這個方法設置了isInterrupted布爾值,如果不測試線程是否中斷,子線程不會中斷
如果線程中有wait()等待或者sleep()休眠
這時調用interrupt方法會拋出一個異常InterruptedException並且清除中斷狀態,子程序繼續運行;
如果線程中沒有等待或者休眠
這時調用interrupt方法會設置中斷狀態(也就是true/false的改變)

測試中斷狀態

public class Demo {
    public static void main(String[] args) {
        InterruptThread t1 = new InterruptThread();
        InterruptThread t2 = new InterruptThread();
        t1.start();
        t2.start();

        for (int i = 0; i < 50; i++) {
            if (i == 25) {
                t1.isOver = true;
                break;
            }
            System.out.println(i + "----");
        }
        System.out.println("主線程結束");
    }
}

class InterruptThread extends Thread{

    public boolean isOver = false;

    @Override
    public synchronized void run() {
        while (!isOver) {
            try {
                wait(); //放棄CPU執行資源
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+ "run");
        }
    }   
}
測試結果:線程1進來帶着鎖遇到wait方法放棄了CPU的執行資源,但是鎖會還回去
緊接着線程2拿着鎖進來,又遇到wait方法,也放棄了CPU的執行資源,相當於兩個線程進入冷凍(中斷)狀態

注意:如果在非同步方法裏調用wait方法,會出現IllegalMonitorStateException異常(對象監視器就是對象鎖)
所以在調用wait方法必須擁有對象鎖
public class Demo {
    public static void main(String[] args) {
        InterruptThread t1 = new InterruptThread();
        InterruptThread t2 = new InterruptThread();
        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 InterruptThread extends Thread{


    @Override
    public synchronized void run() {
        while (true) {
            try {
                wait(); //放棄CPU執行資源
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+ "run");
        }
    }   
}
測試結果:
調用interrupt方法可以消除該狀態,但是碰到wait方法會拋出異常,所以建議進來不要使用interrupt方法
如果要停止線程直接使用標記法來停止,只有遇到了等待狀態時,可以使用該方法強行清除該等待狀態

間隔輸出

需求:
    Person類 姓名 性別
    開啓兩個線程
    一個對Person對象進行賦值
    一個對Person對象進行打印
    一次打印 張三 男
    一次打印 zahngsan nv
    間隔輸出

分析:
    1.先保證要操作都是同一個對象
    2.要保證數據安全需要使用鎖並且要使用同一把鎖
    3.保證操作的邏輯順序要對(先賦值 再打印)用 wait 和 notify
    // Person類
    class Person {
        public String name;
        public String gender;
        // 聲明標記來切換打印和賦值
        public boolean flag = false;
    }

    // 賦值線程
    class SetRunnable implements Runnable{
        // 利用成員變量 操作同一個對象
        private Person p;

        // 定義一個標識  通過改變標識 來進行間隔賦值
        private boolean isTrue = true;
        // 進行賦值
        public SetRunnable() {
            super();
        }
        public SetRunnable(Person p) {
            super();
            this.p = p;
        }

        @Override
        public void run() {
            while (true) {
                // 多個線程操作共享數據 爲了保證兩個鎖對象相同 使用傳進來的對象p
                synchronized (p) {
                    // 先不進入等待 要先進行賦值
                    if (p.flag == true) {
                        // 進行等待
                        try {
                            p.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    // 間隔賦值
                    if (isTrue) {
                        p.name = "張三";
                        p.gender = "男";
                    } else {
                        p.name = "zhangsan";
                        p.gender = "nv";
                    }
                    // 改變標識符
                    isTrue = !isTrue;
                    // 修改標記
                    p.flag = true;
                    // 喚醒線程
                    p.notify();
                }
            }
        }
    }

    // 打印線程   
    class PrintRunnable implements Runnable{
        private Person p;

        public PrintRunnable() {
            super();
        }

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

        @Override
        public void run() {
            while(true) {
                synchronized (p) {
                    if (p.flag == false) {
                        // 進入等待
                        try {
                            p.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println(p.name + "---" + p.gender);
                    // 修改標記
                    p.flag = false;
                    // 喚醒線程
                    p.notify();
                }
            }
        }
    }

    public class Demo06 {
        public static void main(String[] args) {
            // 創建對象 使用同一個對象
            Person person = new Person();
            SetRunnable sr = new SetRunnable(person);
            Thread t1 = new Thread(sr);
            t1.start();
            PrintRunnable pr = new PrintRunnable(person);
            Thread t2 = new Thread(pr);
            t2.start();
        }
    }

間隔輸出邏輯:
    賦值線程需要賦值完畢,打印線程才能去打印
    打印線程需要打印完畢,賦值線程才能去賦值
    賦值的時候打印線程在等待,等賦值完成後才能去打印打印完成後通知賦值線程繼續賦值
實現間隔輸出:
    使用一個標記完成切換wait() 和 notify(),實現上面的邏輯
    兩個線程必須保證使用同一個標記,所以標記要聲明在Person類中
代碼優化:把之前上鎖的代碼封裝到類中,從同步代碼塊封裝成同步方法

修改部分代碼如下(未改同上):
    class Person {
        // 把操作共享數據的部分寫成一個同步方法
        public synchronized void setPerson(String name, String gender) {
            // 等待
            if (flag == true) {
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            this.name = name;
            this.gender = gender;
            // 更改標記
            flag = true;
            // 喚醒線程
            this.notify();
        }

        // 打印方法
        public synchronized void printPerson() {

            if (flag == false) {
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(name + "---" + gender);
            flag = false;
            this.notify();
        }   
    }

    class PrintRunnable implements Runnable{
        @Override
        public void run() {
            while(true) {
                p.printPerson();
            }
        }
    }

    class SetRunnable implements Runnable{
        @Override
        public void run() {
            while (true) {
                if (isTrue) {
                    p.setPerson("張三", "男");
                } else {
                    p.setPerson("zhangsan", "nv");
                }
                // 更改間隔標記
                isTrue = !isTrue;
            }
        }   
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章