当前位置:主页 > java教程 > Java线程的停止

Java线程的停止实现原理详解

发布:2023-04-22 16:15:01 59


我们帮大家精选了相关的编程文章,网友堵鹏举根据主题投稿了本篇教程内容,涉及到Java线程的停止、Java线程原理、Java线程的停止相关内容,已被127网友关注,相关难点技巧可以阅读下方的电子资料。

Java线程的停止

线程停止的原理

使用interrupt来通知,而不是强制

java提供了interrrupt让一个线程来通知另一个线程停止

如果想中断一个线程,但是那个线程不想去中断,那就无能为力,我们没有强制去中断线程的手段,因为线程停止前需要做一定的收尾工作

所以正确停止线程,是如何用interrupt来通知那个线程,以及被停止的线程如何进行配合

如何正确停止线程

在普通情况下停止线程

代码展示

  • 调用interrupt没有作用
  • 下面这段代码,执行interrupt之后,线程并没有被中断
  • 因为被执行的线程并没有相应中断的方式
public class stopThreadWithoutSleep  implements Runnable{
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new stopThreadWithoutSleep());
        thread.start();
        Thread.sleep(1000);
        thread.interrupt();
    }
    @Override
    public void run() {
        int num = 0;
        while(num <= Integer.MAX_VALUE / 2) {
            if (num % 10000 == 0) {
                System.out.println(num + "是10000的倍数");
            }
            num++;
        }
        System.out.println("结束");
    }
}
/*
由于太长,只展示结尾部分的结果 
1073710000是10000的倍数
1073720000是10000的倍数
1073730000是10000的倍数
1073740000是10000的倍数
结束
* */
  • 被执行线程加上相应中断的操作之后
  • 结果可知,被执行线程相应一秒之后就结束了
public class stopThreadWithoutSleep  implements Runnable{
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new stopThreadWithoutSleep());
        thread.start();
        Thread.sleep(1000);
        thread.interrupt();
    }
    @Override
    public void run() {
        int num = 0;
        while(!Thread.currentThread().isInterrupted() && num <= Integer.MAX_VALUE / 2) {
            if (num % 10000 == 0) {
                System.out.println(num + "是10000的倍数");
            }
            num++;
        }
        System.out.println("结束");
    }
}
/*
由于太长,只展示结尾部分的结果
587830000是10000的倍数
587840000是10000的倍数
587850000是10000的倍数
587860000是10000的倍数
结束
* */

在阻塞情况下停止线程

代码展示

  • 中断之后,抛出异常
  • 线程在sleep的过程中,会catch到InterruptedException这个异常,从而相应中断
public class stopThreadWithSleep {
    public static void main(String[] args) {
        Runnable runnable = () -> {
            int num = 0;
            while (num <= 300 && !Thread.currentThread().isInterrupted()) {
                if (num % 100 == 0) {
                    System.out.println(num + "是100的倍数");
                }
                num++;
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };
        Thread thread = new Thread(runnable);
        thread.start();
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        thread.interrupt();
    }
}
/*
* 0是100的倍数
100是100的倍数
200是100的倍数
300是100的倍数
java.lang.InterruptedException: sleep interrupted
   at java.lang.Thread.sleep(Native Method)
   at com.jx.JavaTest.stopThread.stopThreadWithSleep.lambda$main$0(stopThreadWithSleep.java:15)
   at java.lang.Thread.run(Thread.java:748)
* */

线程在每次迭代后都阻塞

  • 代码展示 即使不在while判断是否中断,sleep也能中断异常
public class stopThreadWithSleepEveryLoop {
    public static void main(String[] args) {
        Runnable runnable = () -> {
            int num = 0;
            try {
                while (num <= 10000) {
                    if (num % 100 == 0) {
                        System.out.println(num + "是100的倍数");
                    }
                    num++;
                    Thread.sleep(10);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };
        Thread thread = new Thread(runnable);
        thread.start();
        try {
            Thread.sleep(5000);
        } catch (
                InterruptedException e) {
            e.printStackTrace();
        }
        thread.interrupt();
    }
}
/*
* 0是100的倍数
100是100的倍数
200是100的倍数
300是100的倍数
java.lang.InterruptedException: sleep interrupted
   at java.lang.Thread.sleep(Native Method)
   at com.jx.JavaTest.stopThread.stopThreadWithSleepEveryLoop.lambda$main$0(stopThreadWithSleepEveryLoop.java:15)
   at java.lang.Thread.run(Thread.java:748)
Process finished with exit code 0
* 
* */

当catch写到while内,则不能正常中断

public class CantInterrupt {
    public static void main(String[] args) {
        Runnable runnable = () -> {
            int num = 0;
            while (num <= 10000) {
                if (num % 100 == 0) {
                    System.out.println(num + "是100的倍数");
                }
                num ++;
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        Thread thread = new Thread(runnable);
        thread.start();
        try {
            Thread.sleep(5000);
        } catch (
                InterruptedException e) {
            e.printStackTrace();
        }
        thread.interrupt();
    }
}
/*
* 0是100的倍数
100是100的倍数
200是100的倍数
300是100的倍数
java.lang.InterruptedException: sleep interrupted
   at java.lang.Thread.sleep(Native Method)
   at com.jx.JavaTest.stopThread.CantInterrupt.lambda$main$0(CantInterrupt.java:14)
   at java.lang.Thread.run(Thread.java:748)
400是100的倍数
500是100的倍数
600是100的倍数
700是100的倍数
800是100的倍数
900是100的倍数
Process finished with exit code -1
* */
  • 即使在while的判断条件中,加上检测中断的机制,也不能正常中断
  • 因为java的sleep函数,一旦相应中断,就会将中断的标志位删除
public class CantInterrupt {
    public static void main(String[] args) {
        Runnable runnable = () -> {
            int num = 0;
            while (num <= 10000 && !Thread.currentThread().isInterrupted()) {
                if (num % 100 == 0) {
                    System.out.println(num + "是100的倍数");
                }
                num++;
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        Thread thread = new Thread(runnable);
        thread.start();
        try {
            Thread.sleep(5000);
        } catch (
                InterruptedException e) {
            e.printStackTrace();
        }
        thread.interrupt();
    }
}
/*
0是100的倍数
100是100的倍数
200是100的倍数
300是100的倍数
java.lang.InterruptedException: sleep interrupted
   at java.lang.Thread.sleep(Native Method)
   at com.jx.JavaTest.stopThread.CantInterrupt.lambda$main$0(CantInterrupt.java:14)
   at java.lang.Thread.run(Thread.java:748)
400是100的倍数
500是100的倍数
Process finished with exit code -1
* */

停止线程的最佳实践

  • 在方法签名中抛出异常,在run方法中强制进行try catch
public class StopThreadInProd implements Runnable{
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new StopThreadInProd());
        thread.start();
        Thread.sleep(1000);
        thread.interrupt();
    }
    @Override
    public void run() {
        while (true) {
            System.out.println("start");
            try {
                throwInMethod();
            } catch (InterruptedException e) {
                System.out.println("保存日志/关闭程序");
                e.printStackTrace();
            }
        }
    }
    private void throwInMethod() throws InterruptedException {
            Thread.sleep(2000);
    }
}
/*
* start
保存日志/关闭程序
start
java.lang.InterruptedException: sleep interrupted
   at java.lang.Thread.sleep(Native Method)
   at com.jx.JavaTest.stopThread.StopThreadInProd.throwInMethod(StopThreadInProd.java:26)
   at com.jx.JavaTest.stopThread.StopThreadInProd.run(StopThreadInProd.java:17)
   at java.lang.Thread.run(Thread.java:748)
start
start
Process finished with exit code -1
* 
* */
  • 在catch语句中调用Thread.currentThread().interrupt恢复中断状态
  • 结果:抛出异常,程序结束
public class StopThreadInProd2 implements Runnable{
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new StopThreadInProd2());
        thread.start();
        Thread.sleep(1000);
        thread.interrupt();
    }
    @Override
    public void run() {
        while (true) {
            if (Thread.currentThread().isInterrupted()) {
                System.out.println("Interrupt");
                break;
            }
            reInterrupt();
        }
    }
    private void reInterrupt() {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            e.printStackTrace();
        }
    }
}
/*
java.lang.InterruptedException: sleep interrupted
   at java.lang.Thread.sleep(Native Method)
   at com.jx.JavaTest.stopThread.StopThreadInProd2.reInterrupt(StopThreadInProd2.java:25)
   at com.jx.JavaTest.stopThread.StopThreadInProd2.run(StopThreadInProd2.java:19)
   at java.lang.Thread.run(Thread.java:748)
Interrupt
*
* */
  • 依照上面的代码,如果方法没有没有重新抛出异常
  • 结果:程序抛出异常,但是程序没有停止运行
public class StopThreadInProd2 implements Runnable{
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new StopThreadInProd2());
        thread.start();
        Thread.sleep(1000);
        thread.interrupt();
    }
    @Override
    public void run() {
        while (true) {
            if (Thread.currentThread().isInterrupted()) {
                System.out.println("Interrupt");
                break;
            }
            reInterrupt();
        }
    }
    private void reInterrupt() {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
//            Thread.currentThread().interrupt();
            e.printStackTrace();
        }
    }
}
/*
java.lang.InterruptedException: sleep interrupted
   at java.lang.Thread.sleep(Native Method)
   at com.jx.JavaTest.stopThread.StopThreadInProd2.reInterrupt(StopThreadInProd2.java:25)
   at com.jx.JavaTest.stopThread.StopThreadInProd2.run(StopThreadInProd2.java:19)
   at java.lang.Thread.run(Thread.java:748)
*
* */

错误停止的方法

被弃用的stop,suspend和resume方法

  • 使用stop停止线程,会导致线程运行一半突然停止,没办法完成最基本的操作,会造成脏数据
  • 下面这段代码的结果会造成一个连队只有部分人领取到了装备
  • stop是不安全的,会直接停止监视器
  • suspend和resume不会破坏对象,但是会让线程挂起,不释放锁,容易造成死锁
public class StopThread implements Runnable{
    @Override
    public void run() {
        // 模拟指挥军队,一共五个连队,每个连队一百人
        // 以连队为单位发放武器
        for (int i = 0; i < 5; i++) {
            System.out.println("连队" + i + "领取武器");
            for (int j = 0; j < 10; j++) {
                System.out.println(j);
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("连队" + i + "领取完毕");
        }
    }
    public static void main(String[] args) {
        Thread thread = new Thread(new StopThread());
        thread.start();
        try {
            thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        thread.stop();
    }
}
/*
* 连队0领取武器
0
1
2
3
4
5
6
7
8
9
连队0领取完毕
连队1领取武器
0
1
2
3
4
5

Process finished with exit code 0

* */

用volatile设置boolean标记位

  • 下面这段代码,通过改变标志位的值会成功终止线程
public class Volatile implements Runnable {
    private volatile boolean canceled = false;
    @Override
    public void run() {
        int num = 0;
        try {
            while (num <= 10000 && !canceled) {
                if (num % 100 == 0) {
                    System.out.println(num + " 是100的倍数");
                }
                num++;
                Thread.sleep(1);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) throws InterruptedException {
        Volatile v = new Volatile();
        Thread thread = new Thread(v);
        thread.start();
        Thread.sleep(1000);
        v.canceled = true;
    }
}
/*
*0 是100的倍数
100 是100的倍数
200 是100的倍数
300 是100的倍数
400 是100的倍数
500 是100的倍数
600 是100的倍数
Process finished with exit code 0
* 
* */
  • 当陷入阻塞的时候,是无法停止线程的
  • 下面这段代码的运行结果,并没有打印生产者停止运行,说明根本没有执行生产者的finally那部分代码
  • 同时程序也没停止
  • 原因见生产者代码 while循环中的注释
// 模拟生产者和消费者
public class cantStop {
    public static void main(String[] args) throws InterruptedException {
        // 阻塞队列
        // 满了之后,放不进去
        // 空的时候取数据,也会堵塞
        ArrayBlockingQueue storage = new ArrayBlockingQueue(10);
        Producer producer = new Producer(storage);
        Thread producerThread = new Thread(producer);
        producerThread.start();
        Thread.sleep(1000);
        Consumer consumer = new Consumer(storage);
        while (consumer.needMore()) {
            System.out.println(consumer.storage.take() + "被消费");
            Thread.sleep(100);
        }
        System.out.println("消费者不需要更多数据");
        // 消费者不需要数据,让生产者停下来
        producer.canceled = true;
    }
}
// 生产者
class Producer implements Runnable {
    public volatile boolean canceled = false;
    BlockingQueue storage;
    public Producer(BlockingQueue storage) {
        this.storage = storage;
    }
    @Override
    public void run() {
        int num = 0;
        try {
            while (num <= 10000 && !canceled) {
                if (num % 100 == 0) {
                    // 当堵塞队列满了之后,会堵塞在这里,而这段代码没有判断机制
                    storage.put(num);
                    System.out.println("num" + "生产");
                }
                num++;
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println("生产者停止运行");
        }
    }
}
// 消费者
class Consumer {
    BlockingQueue storage;
    public Consumer(BlockingQueue storage) {
        this.storage = storage;
    }
    public boolean needMore() {
        if (Math.random() > 0.9) {
            return false;
        }
        return true;
    }
}
/*
* num生产
num生产
num生产
num生产
num生产
num生产
num生产
num生产
num生产
num生产
0被消费
num生产
消费者不需要更多数据

* 
* */
  • 将上面代码用interrupt进行中断
  • 程序成功停止
public class finxed {
    public static void main(String[] args) throws InterruptedException {
        finxed finxed = new finxed();
        // 阻塞队列
        // 满了之后,放不进去
        // 空的时候取数据,也会堵塞
        ArrayBlockingQueue storage = new ArrayBlockingQueue(10);
        Producer producer = finxed.new Producer(storage);
        Thread producerThread = new Thread(producer);
        producerThread.start();
        Thread.sleep(1000);
        Consumer consumer = finxed.new Consumer(storage);
        while (consumer.needMore()) {
            System.out.println(consumer.storage.take() + "被消费");
            Thread.sleep(100);
        }
        System.out.println("消费者不需要更多数据");
        // 消费者不需要数据,让生产者停下来
        producerThread.interrupt();
    }
    class Producer implements Runnable {
        public volatile boolean canceled = false;
        BlockingQueue storage;
        public Producer(BlockingQueue storage) {
            this.storage = storage;
        }
        @Override
        public void run() {
            int num = 0;
            try {
                while (num <= 10000 && !Thread.currentThread().isInterrupted()) {
                    if (num % 100 == 0) {
                        storage.put(num);
                        System.out.println("num" + "生产");
                    }
                    num++;
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                System.out.println("生产者停止运行");
            }
        }
    }
    class Consumer {
        BlockingQueue storage;
        public Consumer(BlockingQueue storage) {
            this.storage = storage;
        }
        public boolean needMore() {
            if (Math.random() > 0.9) {
                return false;
            }
            return true;
        }
    }
}
/*
* 2100被消费
num生产
2200被消费
num生产
2300被消费
num生产
消费者不需要更多数据
生产者停止运行
java.lang.InterruptedException
   at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.reportInterruptAfterWait(AbstractQueuedSynchronizer.java:2014)
   at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2048)
   at java.util.concurrent.ArrayBlockingQueue.put(ArrayBlockingQueue.java:353)
   at com.jx.JavaTest.stopThread.volatiledmo.finxed$Producer.run(finxed.java:51)
   at java.lang.Thread.run(Thread.java:748)
Process finished with exit code 0
* 
* */

interrupt源码查看

  • 这段代码做的都是一些判断,真正执行中断的代码时interrupt0
  • interrupt0是native代码
public void interrupt() {
    if (this != Thread.currentThread())
        checkAccess();
    synchronized (blockerLock) {
        Interruptible b = blocker;
        if (b != null) {
            interrupt0();           // Just to set the interrupt flag
            b.interrupt(this);
            return;
        }
    }
    interrupt0();
}
private native void interrupt0();

interrupt相关函数练习

  • isInterrupted获取中断标志,获取的是前面的线程
  • interrupted获取中断标志并重置,只关心执行的线程,所以下面代码执行的是main线程
public class InterruptedTest {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {

                }
            }
        });
        thread.start();
        thread.interrupt();
        // 获取中断标志
        System.out.println(thread.isInterrupted()); // true
        // 获取中断标志并重置
        System.out.println(thread.interrupted()); //false
        System.out.println(Thread.interrupted()); // false
        System.out.println(thread.isInterrupted()); //true
        thread.join();
        System.out.println("over");
    }
}

到此这篇关于Java线程的停止实现原理详解的文章就介绍到这了,更多相关Java线程的停止内容请搜索码农之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持码农之家!


参考资料

相关文章

网友讨论