线程终止、请求丢失。

本文介绍了一个关于异步IO的问题及其解决方案。作者在编写一个小程序时遇到问题:当使用WSARecv()发起异步请求后,无法获取到数据。通过逐步排查,最终发现是由于发起IO请求的线程提前终止导致数据丢失,并通过使用QueueUserWorkItem()来替代自定义线程解决了该问题。
今天写一个小程序,当接收到指定消息时,创建一个SOCKET去连接指定主机,一旦接通,就向主机发送、以及从主机取回一些数据。为了避免阻塞,每当需要连接主机的时候,就创建一个线程,在该线程里调用connect(),连接成功后就用send()发送一些数据,紧接着调用WSARecv()发起一个overlapped的异步取请求,然后线程退出。
结果是,负责监视完成端口的线程,根本取不到数据,只是收到一个SOCKET断开的事件。没办法,只好单步跟踪,奇怪的是,单步跟踪时居然一切正常,该发送的数据发送了,该收到的数据也收到了,百思不得其解……加上一些调试信息,发现WSARecv()会返回WSA_IO_PENDING,这个返回值表示重叠IO已提交,但暂时没有还完成……这个返回值也属于正常情况,但偏偏就是收不到数据。< /div>
想起单步跟踪时,一切都正常,而单步跟踪与正常运行的唯一差别,可以说就是单步跟踪时执行每条语句时有延时,于是在WSARecv()前面加上一条Sleep(1000),再运行程序,惊奇地发现数据收到了,而调试信息打印的WSARecv()返回值是 0……
为什么有了延时就会成功呢,左思右想,忽然从脑海深处闪出一条很久以前的记忆:当发起IO请求的线程终止后,该IO请求将被Windows丢弃!心中一阵狂喜,几乎可以肯定就是这个原因了,马上修改代码,用QueueUserWorkItem()并且传递 WT_EXECUTEINIOTHREAD参数来取代自己创建的线程,运行后果然一切正常。
<think>我们正在讨论Java中线程的启动和终止。用户要求了解如何启动和终止线程。根据提供的引用,我们可以总结如下: 启动线程: 1. 创建线程对象:可以通过继承Thread类并重写run()方法,或者实现Runnable接口然后传递给Thread构造函数。 2. 调用start()方法:启动线程,JVM会自动调用run()方法。 终止线程: 引用[1]提到:线程在run()方法执行完毕后会正常结束。但有时我们需要提前终止线程。 引用[2]指出:Java虚拟机(JVM)无法直接终止一个线程,调用thread.stop()方法是在用户态发出中断请求,真正的终止需要操作系统内核配合。但stop()方法已被废弃,因为它可能导致数据不一致等问题。 引用[3]提供了几种终止线程的方式: - 使用interrupt()方法设置中断标志,线程在运行中检查中断标志并退出。 - 在阻塞操作中(如sleep、wait),捕获InterruptedException异常,然后退出。 - 使用自定义的标志位(如volatile boolean)来控制循环。 - 注意:stop方法不安全,已被废弃。 引用[4]提到了在Spring Boot中启动守护线程,通过ApplicationRunner和@Async注解,但这不是标准Java的内容。 因此,回答将围绕: 1. 启动线程:两种方式(继承Thread和实现Runnable),调用start()。 2. 终止线程:安全的方式(中断标志和自定义标志)和不安全的方式(stop方法,已废弃)。 同时,根据用户要求,生成相关问题。</think>### Java线程启动与终止详解 #### 一、启动线程的两种核心方式 1. **继承`Thread`类** ```java class MyThread extends Thread { @Override public void run() { System.out.println("线程执行中..."); } } // 启动线程 new MyThread().start(); // 调用start()而非run(),否则会阻塞主线程[^1] ``` 2. **实现`Runnable`接口**(推荐:避免单继承限制) ```java class MyRunnable implements Runnable { @Override public void run() { System.out.println("线程执行中..."); } } // 启动线程 new Thread(new MyRunnable()).start(); ``` **关键区别**: - `start()`:JVM创建新线程并异步执行`run()` - `run()`:直接调用会在**当前线程**同步执行 --- #### 二、安全终止线程的4种方式 1. **自然结束**(推荐) 线程在`run()`方法执行完毕后自动终止[^1]: ```java public void run() { while (任务未完成) { // 执行任务 } // 循环结束则线程终止 } ``` 2. **中断标志`interrupt()`**(最安全) ```java public void run() { while (!Thread.currentThread().isInterrupted()) { try { Thread.sleep(1000); // 阻塞操作可响应中断 } catch (InterruptedException e) { Thread.currentThread().interrupt(); // 重新设置中断标志 break; // 退出循环 } } } // 外部调用:thread.interrupt(); ``` - 原理:通过`isInterrupted()`检测中断标志,非强制终止[^2][^3] - 优势:线程可安全清理资源后退出 3. **自定义标志位**(需配合`volatile`) ```java class MyThread extends Thread { private volatile boolean stopped = false; // 保证可见性 public void run() { while (!stopped) { // 执行任务 } } public void stopGracefully() { stopped = true; } } ``` 4. **废弃方法`stop()`(禁止使用)** - 问题:立即终止线程,可能导致对象状态不一致或资源泄露[^3] - 替代方案:始终使用中断标志或自定义标志 --- #### 三、终止线程的注意事项 1. **阻塞操作处理** - 当线程在`sleep()`/`wait()`时,调用`interrupt()`会触发`InterruptedException` - 必须在捕获异常后**再次调用`interrupt()`**恢复中断状态[^3]: ```java try { Thread.sleep(1000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); // 重置中断标志 } ``` 2. **守护线程(Daemon Thread)** - 设置:`thread.setDaemon(true)`(需在`start()`前调用) - 特性:JVM在所有非守护线程结束时自动终止守护线程[^4] 3. **Spring Boot中的线程管理** 通过`@Async`注解实现异步任务,使用`ApplicationRunner`控制生命周期[^4]: ```java @Component public class MyTask implements ApplicationRunner { @Async public void run() { /* 后台任务 */ } } ``` --- #### 四、最佳实践总结 | **场景** | **推荐方案** | **风险** | |------------------------|-----------------------------|-----------------------------| | 循环任务 | `while(!isInterrupted())` | 忽略中断可能导致无法终止 | | 阻塞操作(I/O、sleep) | 捕获`InterruptedException` | 未重置中断标志会丢失中断请求 | | 第三方库控制线程 | 自定义`volatile`标志位 | 标志位未同步导致状态不一致 | | 需要即时终止 | 无完全安全方案 | 强制终止必然带来资源泄露风险 | > **关键原则**: > 1. 优先让线程自然结束 > 2. 需要主动终止时,使用`interrupt()`+状态检查 > 3. 绝对避免使用`stop()`/`suspend()`/`resume()`
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值