优雅的启动和关闭线程

1、启动线程:start和run的区别

启动线程时,要调用start方法。而start方法会去调用run方法,那么为什么不直接调用run方法?

public static void main(String[] args) {
        Runnable runnable = () -> {
            System.out.println(Thread.currentThread().getName());
        };
        runnable.run();
        new Thread(runnable).start();
    }

运行结果:main Thread-0
run方法只是一个普通的方法,执行时是由主线程去调用的,并没用使用子线程,start方法才是真正用子线程调用的。

start方法执行流程:

  • 1、检查线程状态,只有new状态下的线程才能继续,否则会抛出异常
  • 2、被加入线程组
  • 3、调用start0()方法启动线程

那么,一个线程调用两次start会发生什么?

IllegalThreadStateException
IllegalThreadStateException

在调用start方法时会进行状态检测,包括新建,就绪,阻塞,等待、计时等待、终止,第二次调用start() 方法的时候,已经被start的线程已经不再是(NEW)状态了,所以无论如何是不能再次启动的。

2、终止线程最优雅的方式——interrupt

在这里插入图片描述
网上基本上流传着三种终止线程的方式:常规方式:interrupt;有点逼格的方式:volatile;已经淘汰的方式:stop
其中,volatile方式最有迷惑性,在有些情况下会造成bug。

最优雅的方式:interrupt

interrupt方法并不是一种强制性终止线程的方式,它相当于是一个信号,interrupt方法触发之后,子线程是否停止,是子线程说的算。

public class RightWayStopThreadInProd implements Runnable{
    @Override
    public void run() {
        while (true){
            System.out.println("子线程执行任务");
            try {
                throwInMethod();
            } catch (InterruptedException e) {
                e.printStackTrace();
                System.out.println("子线程收到interrupt中断信息");
            }
        }
    }
    private void throwInMethod() 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();
    }
}

运行结果:
在这里插入图片描述
可以看到,interrupt之后,并没有终止线程,而实抛出了interrupt的异常。这正验证了,interrupt只是一个信号,在捕捉到interrupt信息之后,可以根据具体的业务,决定是否真正停止线程。
例如,若要终止线程,可以加一个break。
在这里插入图片描述

为什么不用volatile?

volatile的通常做法是声明一个Boolean类型的标志位,当需要终止线程时,改变这个标志位,从而跳出循环。
为什么不用volatile呢?
设想,假如,在while循环内发生了阻塞,那么这个线程就永远无法结束。。

//生产者、消费者
//生产者生产速度快,消费者消费速度慢,当产品过剩,生产者阻塞,消费者在特定情况下停止消费,此时发生阻塞
ublic class WrongWayVolatileCantStop {
    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.needMoreNums()){
            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 <= 100000 && !canceled) {
                if (num % 100 == 0) {
                    //最后此处发生阻塞,导致无法结束
                    storage.put(num);
                    System.out.println(num + "是100的倍数。");
                }
                num++;
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            System.out.println("生产者结束运行");
        }
    }
}

class Consumer{
    BlockingQueue storage;
    public Consumer(BlockingQueue storage){
        this.storage=storage;
    }

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

运行结果:阻塞之后,程序无法结束
阻塞之后,程序没有结束

为什么不用stop?

Oracle官方解释:https://docs.oracle.com/javase/1.5.0/docs/guide/misc/threadPrimitiveDeprecation.html

因为它本质上是不安全的。停止线程会使它解锁它已锁定的所有监视器。(当ThreadDeath异常在堆栈中传播时,监视器将被解锁 。)如果以前由这些监视器保护的任何对象处于不一致状态,则其他线程现在可能会以不一致状态查看这些对象。据说这类物体已损坏。当线程对损坏的对象进行操作时,可能会导致任意行为。此行为可能是微妙的,难以检测,或者可能是明显的。与其他未经检查的异常不同,它会 ThreadDeath无声地杀死线程。因此,用户没有警告其程序可能已损坏。在实际损坏发生后的任何时间,甚至未来数小时或数天,腐败都会显现出来。

关于interrupt和isInterrupt
public static void main(String[] args) {
        Runnable runnable = () -> {
            System.out.println(Thread.currentThread().getName());
        };
        
        Thread thread = new Thread(runnable);
        thread.start();

        System.out.println(thread.isInterrupted());
        thread.interrupt();
        System.out.println(thread.isInterrupted());
        System.out.println(Thread.interrupted());
        System.out.println(thread.interrupted());
    }

执行结果:
在这里插入图片描述
第一个和第二个比较明显,isInterrupted就是判断一下当前线程的状态
而第三和第四个返回的结果都是false。interrupted是一个静态方法,它不管是谁调用的,返回的都是执行这个方法的线程的状态,而执行它的线程是主线程,没有被中断,因此返回的都是false。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Qt中,可以通过QThread类来创建和管理线程。要关闭所有线程,可以使用QThread::terminate()函数来终止所有线程。这会强制终止线程并释放相关资源,但可能会导致一些问题,因为它不会清理线程中的资源或进行清理工作。 为了优雅关闭线程,可以在线程中实现一个停止标志,并在主线程中设置该标志以通知线程停止。线程可以在自己的运行循环中检查该标志,并在标志被设置时自行退出。这样可以确保线程安全地退出并清理资源。 以下是一个示例,展示如何在Qt中优雅关闭线程: ```c++ #include <QtCore> class MyThread : public QThread { public: MyThread(QObject *parent = nullptr) : QThread(parent) { m_stop = false; } void stop() { m_stop = true; } protected: void run() override { while (!m_stop) { // 在这里执行线程任务 // 检查停止标志,如果设置了就退出 if (m_stop) break; } } private: bool m_stop; }; int main(int argc, char *argv[]) { QApplication app(argc, argv); MyThread thread1; MyThread thread2; thread1.start(); thread2.start(); // 在这里停止所有线程 thread1.stop(); thread2.stop(); thread1.wait(); thread2.wait(); return app.exec(); } ``` 在上面的示例中,我们定义了一个名为MyThread的线程类,并在其中实现了一个停止标志。当停止标志被设置时,线程将自行退出。在主线程中,我们创建了两个线程并启动它们。然后,我们在主线程中设置停止标志以通知线程停止,并等待所有线程结束。 请注意,这只是一个示例,实际实现中可能需要更多的代码来确保线程安全地退出并清理资源。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值