线程的取消与关闭

线程的取消与关闭

1. 处理不可中断的阻塞

在Java库中,许多可阻塞的方法都是通过提前返回或者抛出InterruptedException来相应中断请求的,然而并非所有的可阻塞方法或者阻塞机制都能相应中断。如果一个线程由于可执行同步的Socket IO或者等待获得内置锁而阻塞,那么中断请求只能设置线程的中断状态,除此之外没有其他任何作用。对于那些由于执行不可中断操作而被阻塞的线程,可以使用类似于中断的手段来停止这些线程,但这要求我们必须知道线程阻塞的原因。
1. Socket I/O
在服务应用程序中,最常见的阻塞I/O形式就是对套接字进行读取和写入,虽然InputStream和OutputStream中的read和write等方法都不会响应中断,但通过关闭底层的套接字,可以使得由于执行read或者write等方法而阻塞的线程抛出一个SocketException
2. Selector 异步I/O
如果一个线程在调用Selector.select方法时阻塞了,那么调用close或者wakeup方法会使线程抛出CloseSelectorException并提前返回
3. 获取某个锁
如果一个线程由于等待某个内置锁而阻塞,那么将无法响应中断。因为线程认为它肯定能获得锁,所以将不会理会中断请求。但是在Lock类中提供了lockInterruptibly方法,该方法允许在等待一个锁的同时仍能响应中断。

2. 采用newTaskFor来封装非标准的取消

当把一个Callable提交给ExecutorService时,submit方法会返回一个Future,我们可以通过这个Future来取消任务。newTaskFor是一个工厂方法,它将创建Future来代表任务。newTaskFor还能返回一个RunnableFuture接口,该接口扩展了Future和Runnable。
通过定制表示任务的Future可以改变Future.camcel行为,例如,定制的取消代码可以实现日志记录或者收集取消操作的统计信息,以及取消一些不响应中断的操作等等。

3. shutdownNow的局限性

当通过shutdownNow来强行关闭ExecutorService时,它会尝试取消正在执行的任务,并返回所有已提交但尚未开始的任务,从而将这些任务写入日志或者保存起来以便之后进行处理。
然而,我们无法通过常规的方法来找出哪些任务已经开始但尚未结束,这就意味着我们无法在关闭过程中知道正在执行的任务状态,除非任务本身会执行某种检查。

import java.util.*;
import java.util.concurrent.AbstractExecutorService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * 定制的执行池,执行shutdownNow强制关闭线程池时,
 * 会返回未执行和执行中的线程
 */
public class TrackingExecutor extends AbstractExecutorService {

    private final ExecutorService exec;

    private final Set<Runnable> tasksCancelledAtShutdown =
            Collections.synchronizedSet(new HashSet<Runnable>());

    public TrackingExecutor() {
        exec = Executors.newCachedThreadPool();
    }

    public List<Runnable> getCancelledTasks() {
        if (!exec.isTerminated()) {
            throw new IllegalStateException();
        }
        return new ArrayList<Runnable>(tasksCancelledAtShutdown);
    }

    /**
     * 定制execute,返回中断以及未执行的Runnable
     * @param command
     */
    public void execute(final Runnable command) {
        exec.execute(new Runnable() {
            public void run() {
                try {
                    command.run();
                } finally {
                    if (isShutdown() && Thread.currentThread().isInterrupted()) {
                        tasksCancelledAtShutdown.add(command);
                    }
                }
            }
        });

    }

    public void shutdown() {
        exec.shutdown();
    }

    public List<Runnable> shutdownNow() {
        return exec.shutdownNow();
    }

    public boolean isShutdown() {
        return exec.isShutdown();
    }

    public boolean isTerminated() {
        return exec.isTerminated();
    }

    public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
        return exec.awaitTermination(timeout, unit);
    }
}

4.JVM关闭

4.1 关闭钩子

在正常关闭中,JVM首先调用所有已注册的关闭钩子,关闭钩子是通过Runtime.addShutdownHook注册的但尚未开始的线程,JVM并不能保证关闭钩子的调用顺序,在关闭应用程序线程时,如果有守护或者非守护线程仍然在运行,那么这些线程接下来将与关闭进程并发执行。当所有的关闭钩子都执行结束时,如果runFinalizersOnExit为true,那么JVM将运行终结器,然后再停止。如果关闭钩子或终结器没有执行完成,那么正常关闭进程挂起并且JVM必须被强行关闭,将被强行关闭时,只会关闭JVM而不会关闭钩子。
关闭钩子可以用于实现服务或者应用程序的清理工作,例如删除临时文件,或者清除无法由操作系统自动清除的资源。

public void start()
{
    Runtime.getRuntime().addShutdownHook(new Thread(){
        public void run()
        {
            try{
                LogService.stop();
            }
            catch(InterruptedException ignored)
            {

            }
        }
    });
}

4.2 守护线程

线程可以分为两种:普通线程和守护线程,在JVM启动时创建的所有线程中,除了主线程以外,其他的线程都是守护线程(例如垃圾回收器以及其他执行辅助工作的线程),当创建一个新线程时,新线程将继承创建它的线程的守护状态,因此在默认情况下,主线程创建的所有线程都是普通线程。
普通线程与守护线程之间的差异仅在于当线程退出时发生的操作。当一个线程退出时,JVM会检查其他正在运行的线程,如果这些线程都是守护线程,那么JVM会正常退出操作,当JVM停止时,所有仍然存在的守护线程都将被抛弃,既不会执行finally代码块,也不会执行回卷栈,而JVM只是直接退出。
我们应尽可能少的使用守护线程,很少有操作能够在不进行清理的情况下被安全的抛弃。

4.3 终结器

当不在需要内存资源时,可以通过垃圾回收器来回收他们,但是对于一些其他资源,例如文件句柄或套接字,当不在需要时,必须显式的交还给操作系统。为了实现这个功能,GC对那些定义了finalize方法的对象会进行特殊处理:在回收器释放他们后,调用他们的finalize方法,从而保证一些持久化的资源被释放。
避免使用终结器

<think> 我们讨论的是Python中线程的启动和关闭方法。根据引用内容,启动线程通常通过创建Thread实例并调用其start()方法。关闭线程则比较复杂,因为线程不能被强制终止,但可以通过设置标志位或异常机制来请求终止。 ### 启动线程 在Python中,启动线程的标准方式是: 1. 继承`threading.Thread`类并重写`run()`方法 2. 实例化线程对象 3. 调用`start()`方法启动线程 示例代码: ```python import threading import time class MyThread(threading.Thread): def run(self): print("线程开始执行") time.sleep(2) print("线程结束") # 创建并启动线程 t = MyThread() t.start() ``` ### 关闭线程 关闭线程有两种推荐方式: #### 方法1:使用标志位(安全退出) ```python class StoppableThread(threading.Thread): def __init__(self): super().__init__() self._stop_event = threading.Event() def stop(self): self._stop_event.set() def run(self): while not self._stop_event.is_set(): print("线程运行中...") time.sleep(0.5) print("线程已安全退出") # 使用示例 t = StoppableThread() t.start() time.sleep(2) t.stop() # 请求停止线程 ``` #### 方法2:使用异步异常(强制退出,引用[2][^2][^4]) ```python import ctypes import inspect def _async_raise(tid, exctype): """引发线程异常""" tid = ctypes.c_long(tid) res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exctype)) if res == 0: raise ValueError("无效的线程ID") elif res != 1: ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, None) raise SystemError("设置异步异常失败") def stop_thread(thread): """停止线程""" _async_raise(thread.ident, SystemExit) # 使用示例 class TestThread(threading.Thread): def run(self): try: while True: print("线程执行中") time.sleep(0.1) except SystemExit: print("线程被强制终止") t = TestThread() t.start() time.sleep(1) stop_thread(t) # 强制停止线程 ``` > **重要提示**:强制终止线程可能导致资源未释放等问题[^3],优先使用标志位方法。 ### 守护线程 设置守护线程可在主线程退出时自动终止: ```python t = MyThread() t.daemon = True # 或t.setDaemon(True) t.start() ``` ### 线程清理 对于需要清理资源的线程(如引用[3][^3]中的Timer),应实现`close()`方法并在退出时调用: ```python class ResourceThread(threading.Thread): def run(self): try: # 执行任务 finally: self.cleanup() def cleanup(self): print("清理线程资源") ``` ### 对比总结 | 方法 | 优点 | 缺点 | |-------------------|--------------------------|--------------------------| | **标志位退出** | 安全可控,资源可正常释放 | 需要线程主动检查退出标志 | | **异步异常终止** | 可立即终止阻塞操作 | 可能导致资源泄漏[^4] | | **守护线程** | 主线程退出自动终止 | 无法控制终止时机 | > 实践建议:业务逻辑中优先使用标志位退出机制,仅在必要时使用异步异常终止[^2][^4]。 --- ### §§相关问题§§ 1. Python线程和进程的区别是什么? 2. 如何实现Python多线程间的数据共享? 3. Python的GIL锁对多线程有什么影响? </think> ### Python线程启动关闭方法详解 #### 一、线程启动方法 1. **标准启动方式** ```python import threading def worker(): print("线程执行中...") # 创建线程对象 t = threading.Thread(target=worker) t.start() # 启动线程 ``` 2. **继承Thread类启动**(引用[1][^1]) ```python class MyThread(threading.Thread): def run(self): print("自定义线程执行中...") t = MyThread() t.start() ``` 3. **守护线程启动** ```python t = threading.Thread(target=worker, daemon=True) t.start() # 主线程退出时自动终止 ``` #### 二、线程关闭方法 1. **标志位退出(推荐)** ```python class StoppableThread(threading.Thread): def __init__(self): super().__init__() self._stop_flag = threading.Event() def stop(self): self._stop_flag.set() # 设置停止标志 def run(self): while not self._stop_flag.is_set(): print("运行中...") time.sleep(1) print("线程安全退出") ``` 2. **异步异常终止**(引用[2][^2][^4]) ```python def stop_thread(thread): """强制终止线程""" tid = ctypes.c_long(thread.ident) exctype = SystemExit ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exctype)) class TestThread(threading.Thread): def run(self): try: while True: print("运行中...") time.sleep(0.5) except SystemExit: print("线程被终止") ``` 3. **定时器线程关闭**(引用[3][^3]) ```python class TimerThread: def __init__(self): self.timer = threading.Timer(5.0, self.task) self.timer.start() def task(self): print("定时任务执行") self.timer = threading.Timer(5.0, self.task) self.timer.start() def close(self): self.timer.cancel() # 取消定时器 ``` #### 三、关键技术对比 | 方法 | 安全性 | 资源释放 | 适用场景 | |-----------------|--------|----------|------------------------| | 标志位退出 | ★★★★★ | 完全释放 | 循环任务 | | 异步异常终止 | ★★☆☆☆ | 可能泄漏 | 紧急终止阻塞操作 | | 定时器cancel() | ★★★★☆ | 完全释放 | Timer类线程 | | 守护线程 | ★★★☆☆ | 自动释放 | 后台非关键任务 | #### 四、最佳实践 1. **资源清理模式** ```python class SafeThread(threading.Thread): def __init__(self): self._stop_event = threading.Event() self.resource = open('file.txt', 'w') # 需要清理的资源 def stop(self): self._stop_event.set() def run(self): try: while not self._stop_event.is_set(): # 工作任务 pass finally: self.resource.close() # 确保资源释放 print("资源已清理") ``` 2. **线程池管理** ```python from concurrent.futures import ThreadPoolExecutor with ThreadPoolExecutor(max_workers=4) as executor: future = executor.submit(lambda: print("线程任务")) # 退出with块时自动关闭所有线程 ``` > **重要提示**:强制终止线程(如`stop_thread()`)可能导致资源泄漏[^3][^4],仅在必要时使用 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值