多线程编程之——终止(打断)正在执行中的线程

这篇具有很好参考价值的文章主要介绍了多线程编程之——终止(打断)正在执行中的线程。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

多线程编程之——终止(打断)正在执行中的线程

  • ps:文字有点多,想看结果的,直接跳转:《二》

一、基础知识

1、我们基于spring开发,把线程都交给spring

  • 把线程交给spring管理好不好?

将线程交给Spring管理是一个常见的做法,特别是在基于Spring的应用程序中。通过将线程纳入Spring的管理范围,你可以利用Spring的依赖注入和生命周期管理功能,更好地控制线程的生命周期和资源。这样做也有助于避免手动管理线程带来的潜在问题,比如线程泄露和资源管理不当。但是需要注意的是,要确保在使用Spring管理线程时,遵循最佳实践,避免出现死锁和性能问题。

还有个好处:可以把spring的bean注入到线程中,然后直接使用。(如注入service完成查询数据)

如果把线程交给spring管理,注入线程的地方全是单例,如果要使用这个线程来完成两个不同的任务,需要把单例变成多例模式,spring多例模式见:SpringBoot实现多例模式

  • 一个线程默认当做执行一个任务的最小单元

所以,当你需要执行一个任务的时候,应该把参数封装到线程中,然后在线程中去执行你的任务(方法)

2、启动Thread线程是调用start还是run?

在Java中,启动一个Thread线程应该调用start方法而不是run方法。调用start方法会使Thread进入就绪状态,并让系统调度器来调用run方法。直接调用run方法只会在当前线程中执行run方法的代码,而不会创建新的线程。因此,正确的做法是调用start方法来启动一个新的线程。

3、打断执行中的线程

  • 如果我new了一个Thread线程,它一直在执行中(无限阻塞),我后面调用了interrupt打断线程,这个Thread线程会一直阻塞还是结束任务?

如果你在一个线程对象上调用interrupt方法,会设置线程的中断状态,并且如果线程正在阻塞中(比如在sleep、wait、join等方法中),它会抛出InterruptedException并清除中断状态。但如果线程正在执行普通的任务,它不会自动停止,而是需要在任务中检查中断状态并做出相应的处理。因此,你需要在任务中定期检查线程的中断状态,并在适当的时候结束任务。

  • 我调用interrupt的线程,如果一直处于睡眠中,jvm会自动回收吗?

当你调用一个处于睡眠状态的线程的interrupt方法时,它会抛出InterruptedException并清除中断状态,但并不会导致线程被自动回收。处于睡眠状态的线程会继续睡眠直到其睡眠时间结束或者被唤醒。如果你希望中断一个处于睡眠状态的线程并结束它的任务,你需要在任务中捕获InterruptedException并做出相应的处理。

  • 捕获InterruptedException并在捕获到异常时结束任务或者进行清理工作。示例:
public class MyTask implements Runnable {
    public void run() {
        try {
            // 在这里调用sleep方法使线程进入睡眠状态(模仿无限阻塞)
            Thread.sleep(100000);
        } catch (InterruptedException e) {
            // 捕获InterruptedException并做出相应的处理
            // 可以结束任务或者进行清理工作
            Thread.currentThread().interrupt(); // 重新设置中断状态
            return; // 结束任务
        }
        // 其他任务逻辑
    }
}

在上面的示例中,当线程处于睡眠状态时,如果外部调用了interrupt方法,线程会抛出InterruptedException,然后在catch块中重新设置中断状态并结束任务。
要想终止线程,必须在线程中调用:Thread.currentThread().interrupt();方法,而不是外部调用。

二、打断线程

1、打断new出来的线程

  • 也可以把线程交给spring管理
  • 直接new一个线程,然后在需要打断的地方进行打断,示例:
package com.cc.jschdemo.terminationThread;

/**
 * <p>基本打断</p>
 *
 * @author cc
 * @since 2023/11/23
 */
public class BasicInterrupt {

    /**
     * 终止
     * @param args
     */
    public static void main(String[] args) {
        //t1:模仿执行任务的线程
        Thread t1 = new Thread(() -> {
            try {
                //步奏1
                Thread.sleep(1000);
                System.out.println("完成:步奏1");

                //步奏2(模仿阻塞)
                while (true) {
                    Thread.sleep(1000);
                    System.out.println("完成:步奏2");
                }
            }catch (InterruptedException e) {
                e.printStackTrace();
                //捕获异常:InterruptedException,然后打断
                //☆☆打断,终止当前线程☆☆
                Thread.currentThread().interrupt();
            }
        });

        //t2:模仿其他操作,打断线程
        Thread t2 = new Thread(() -> {
            try {
                Thread.sleep(3100);
                System.out.println("打断线程!");
                //打断t1线程
                t1.interrupt();
            }catch (Exception e) {
                e.printStackTrace();
            }
        });

        //启动两个线程
        t1.start();
        t2.start();
    }
}
  • 测试结果:步奏1执行1次,步奏2执行两次,然后t1线程就会被t2线程终止
  • 线程被打断会抛出错误:java.lang.InterruptedException: sleep interrupted

多线程编程之——终止(打断)正在执行中的线程

2、打断注入spring的线程

  • 新建线程,注入spring中
  • ps:注意,注入spring的线程是单例的,实际使用过程中,我们都是一个线程一个任务,如果需要不同的对象,可以开启spring的多例,见:SpringBoot实现多例模式
package com.cc.jschdemo.terminationThread;

import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.stereotype.Component;

/**
 * <p>注入spring的线程</p>
 *
 * @author --
 * @since 2023/11/24
 */
@EqualsAndHashCode(callSuper = true)
@Data
@Component
public class SpringThread extends Thread{

    //1可以注入Spring的Bean,然后直接使用
//    @Resource
//    private IBasicInterruptService basicInterruptService;

    //2可以直接传入执行任务的参数(可多个)
    private String footwork;

    /**
     * 具体要执行的任务
     */
    @Override
    public void run() {
        try {
            //步奏1
            Thread.sleep(1000);
            System.out.printf("完成:%s 1 %n", footwork);

            //步奏2(模仿阻塞)
            while (true) {
                Thread.sleep(1000);
                System.out.printf("完成:%s 2 %n", footwork);
            }
        }catch (InterruptedException e) {
            e.printStackTrace();
            //捕获异常:InterruptedException,然后打断
            //☆☆打断,终止当前线程☆☆
            Thread.currentThread().interrupt();
        }
    }
}
  • 创建任务,模仿打断
    //可以直接注入线程
    @Resource
    private SpringThread springThread;
    
    //模仿创建任务、打断
    @Test
    public void test07()throws Exception{
        try {
            ///传入执行任务,需要的参数
            springThread.setFootwork("步奏");
            //启动线程
            springThread.start();

            Thread.sleep(3100);
            System.out.println("打断线程!");
            //打断t1线程
            springThread.interrupt();
        }catch (Exception e) {
            e.printStackTrace();
        }
    }
  • 结果:和《打断new出来的线程》一模一样

多线程编程之——终止(打断)正在执行中的线程

3、打断CompletableFuture启动的线程

  • CompletableFuture的cancel(true)方法无法打断
  • 调用线程的Thread task1,的interrupt也无法打断
  • CompletableFuture.runAsync(task1)启动线程task1的底层逻辑是:CompletableFuture调用ForkJoinPool线程池启动一个线程(A)去执行task1任务。
  • 所以我们要打断的不是task1,而是要打断A
package com.cc.jschdemo.terminationThread;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.CompletableFuture;

/**
 * <p></p>
 *
 * @author --
 * @since 2023/11/25
 */
@RestController
@RequestMapping("/completableFuture")
public class CompletableFutureThread {

    //这里使用标记打断的话,每个CompletableFuture都需要一个标记,可以(CompletableFuture和标记)一起缓存下来。
    boolean flagTask1 = false;
    boolean flagTask2 = false;

    //模仿任务1线程,也可以是:Runnable
    Thread task1 = new Thread(() -> {
        try {
            //模仿线程无限阻塞(while (true))
            while (true) {
                System.out.println("任务1执行...");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }

                //检查终止状态,如果终止就报错。true已中断;false未中断
                // 这里获取到的是:CompletableFuture启动线程池的线程池的线程。就是执行task1的线程
                boolean interrupted = Thread.currentThread().isInterrupted();
                // 所以interrupted不能用来终止task1

                //这里使用标记来终止task1(可以根据业务,在需要终止的地方设置这个打断。)
                if (flagTask1) {
                    //打断:CompletableFuture启动线程池的线程池的线程
                    Thread.currentThread().interrupt();
                }
            }
        }catch(Exception e){
            e.printStackTrace();
        }
    });

    //任务2
    Thread task2 = new Thread(() -> {
        try {
            //模仿线程阻塞
            while (true) {
                System.out.println("任务2执行...");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }

                //这里使用标记来终止task1
                if (flagTask2) {
                    Thread.currentThread().interrupt();
                }
            }
        }catch(Exception e){
            e.printStackTrace();
        }
    });

    //缓存CompletableFuture
    CompletableFuture<Void> future1;
    CompletableFuture<Void> future2;

    //启动任务
    @GetMapping
    public void test08()throws Exception{
        //实际使用中,可以使用循环添加CompletableFuture,然后记录下future(缓存),在要中断的逻辑中调用cancel
        //1开启task任务。这里实际是CompletableFuture调用ForkJoinPool线程池启动一个线程去执行task1任务。
        //要想终止任务,要终止CompletableFuture调用ForkJoinPool线程池创建的线程,而非task1线程。
        future1 = CompletableFuture.runAsync(task1);
        future2 = CompletableFuture.runAsync(task2);
    }

    //终止任务
    @PostMapping
    public void stop() {
        //3取消任务
        // 参数true表示尝试中断任务执行
        // cancel方法会尝试取消任务的执行,参数true表示尝试中断任务执行。
        // 成功取消返回true,否则返回false。
        // 需要注意的是,cancel方法并不会直接中断正在执行的线程,而是会尝试取消任务的执行,
        // 具体是否成功取决于任务的实现。

        //终止方式1:使用cancel终止:无法终止task1
        boolean cancel1 = future1.cancel(Boolean.TRUE);
        System.out.println("任务1终止:" + (cancel1 ? "成功1" : "失败1"));
        //终止方式2:打断线程task1:无法终止task1
        task1.interrupt();

        //睡5s,让任务跑5s,看终止方式1、2是否能终止,测试结果为:不能终止
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        //终止方式3:直接标记打断。CompletableFuture调用ForkJoinPool线程池生成的线程
        // 真正的终止task1
        flagTask1 = true;
    }
}



  • 测试

  • 终止方式1/2都执行了。但是任务没有终止

多线程编程之——终止(打断)正在执行中的线程

  • 终止方式3执行后,才真正的终止了。

多线程编程之——终止(打断)正在执行中的线程文章来源地址https://www.toymoban.com/news/detail-747217.html

到了这里,关于多线程编程之——终止(打断)正在执行中的线程的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 学习系统编程No.29【线程执行过程之页表详解】

    北京时间:2023/7/3/14:09,刚睡醒,放假在家起床时间确实不怎么好调整,根本固定不了一点,当然通俗点说也就是根本起不来,哈哈哈,已经很少见到那种7点起来码字的情形了,再加上在家里,琐碎的事情很多,不像是在学校,除了睡觉吃饭就没什么其它事情需要我去花费精

    2024年02月12日
    浏览(32)
  • QT中的耗时操作放到子线程中执行

    代码中有sleep的话,如果不放到子线程中执行,会将主线程卡死。 子线程的.h文件 子线程的.cpp文件 调用的地方的.h文件 调用的地方的.cpp文件

    2024年02月16日
    浏览(43)
  • Java多线程编程中的线程同步

    基本概念: ​ 线程同步是多线程编程中的一个重要概念,用于控制多个线程对共享资源的访问,以防止数据的不一致性和并发问题。 在多线程环境下,多个线程同时访问共享资源可能导致数据的竞争和不正确的结果。 是确保多个线程按照特定的顺序和规则访问共享资源,以

    2024年02月13日
    浏览(47)
  • Java多线程编程中的线程死锁

    ​ 在多线程编程中,线程死锁是一种常见的问题,它发生在两个或多个线程互相等待对方释放资源的情况下,导致程序无法继续执行 。本文将介绍线程死锁的概念、产生原因、示例以及如何预防和解决线程死锁问题。 线程死锁的概念 ​ 线程死锁是指两个或多个线程被阻塞

    2024年02月12日
    浏览(37)
  • git pull的时候:您对下列文件的本地修改将被合并操作覆盖,请在合并前提交或贮藏您的修改。 正在终止

    使用git pull的时候报错: 发生这种情况一般都是由于别人修改了文件并提交了push,你没有pull别人修改的代码你也修改了同一个文件 一般这个时候有两个解决方法: 保留你文件的修改 将所有未提交的修改(工作区和暂存区)保存至堆栈中 在使用git pull命令就会发现没有报错

    2024年02月12日
    浏览(42)
  • 编程(39)----------多线程中的锁

    假设一个这样的场景: 在多线程的代码中, 需要在不同的线程中对同一个变量进行操作. 那此时就会出现问题: 多线程是并发进行的, 也就是说代码运行的时候, 俩个线程会同时对一个变量进行操作, 这样就会涉及到多线程的安全问题: 在这个代码中, 两个线程会分别对count进行自

    2024年02月07日
    浏览(45)
  • 在JavaScript中如何终止程序的执行

    在JavaScript编程中,有时候我们需要在特定条件下终止程序的执行,即提前退出程序。JavaScript提供了几种方法来实现这个目标。下面将介绍三种常用的方法:使用return语句、使用throw语句和使用process.exit()函数。 使用return语句 在函数中,可以使用return语句来提前退出程序的执

    2024年02月03日
    浏览(35)
  • C#多线程精解:优雅终止线程的实用方法与技巧

      概述: 在C#多线程编程中,合理终止线程是关键挑战。通过标志位或CancellationToken,实现安全、协作式的线程终止,确保在适当时机终止线程而避免资源泄漏。 在C#多线程编程中,有时需要终止正在运行的线程,例如在用户取消操作、程序关闭等情况下。 线程终止通常涉及

    2024年02月19日
    浏览(38)
  • Java 终止线程的几种方式

    所谓正常运行结束,就是程序正常运行结束,线程自动结束。 一般run()方法执行完,线程就会正常结束,然而,常常有些线程是伺服线程。他们需要长时间的运行,只有在外部某些条件满足的情况下,才能关闭这些线程。使用一个变量来控制循环,例如:最直接的方法就是设

    2024年02月07日
    浏览(53)
  • C# 中的多线程和异步编程

    最近在看代码的过程中,发现有很多地方涉及到多线程、异步编程,这是比较重要且常用的知识点,而本人在这方面还理解尚浅,因此开始全面学习C#中的多线程和异步编程,文中部分内容摘抄自一位前辈的网站:网址链接,为了更便于理解和学习,本人还在个别地方做了一

    2023年04月08日
    浏览(50)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包