原文:《Java Thread Primitive Deprecation》
为什么 Thread.stop 被废弃了?
Thread.stop 不安全。
- Stop 一个线程会导致它释放所持有的锁(monitor)。(ThreadDeatch 抛出后会释放 monitor 的锁。)
- 这可能会导致之前被 monitor 保护的对象处于 不一致的状态,并被其它线程看到。也就是说,这些 对象被损坏 了。
- 线程操作被损坏的对象会导致不可预料的行为。
- 这些行为可能很微妙难以被察觉,也可能非常明显。与其它异常不同的是,ThreadDeatch 异常会悄悄地杀掉线程,用户感知不到程序已被损坏。这种程序损坏可能在错误发生后任何时候显现,甚至是几小时、几天。
为什么 我不能捕获 ThreadDeath 异常,并修复损坏的对象?
理论上,也许是可以的。但这会使编写多线程任务代码非常复杂,几乎不可能实现。因为:
- 线程可能在任何地方抛出 ThreadDeatch 异常。必须非常详尽地分析所有同步方法和代码块,来处理这些情况。
- 线程在处理第一个 ThreadDeatch 异常期间(catch 或 finally),可能会再次抛出 ThreadDeatch 异常(嵌套异常)。你的异常处理操作必须不断重复,直到成功。这会导致代码非常复杂。
所以这种方案是不切实际的!
Thread.stop(Throwable) 如何?
上述提到的所有问题它都有。此外,此方法一般用于生成一个 目标线程未准备好处理的 异常(包括目标线程不可能抛出的一些 受检异常)。如,以下示例中方法的行为等价于 throw 操作,但它规避了编译器对受检异常的检查保障 —— 调用方法必须声明所有可能抛出的受检异常。
(即,这是一种不安全的错误代码模式。)
void sneakyThrow(Throwable t) { Thread.currentThread().stop(t); }
我该用什么方法替代 Thread.stop ?
绝大多数使用 stop 的代码应改为:
- 修改一些标记性变量的值 以指示目标线程应停止运行;
- 目标线程应定期检查这些标记变量;当检测到变量值指示应停止运行时,再有条不紊地从运行方法中自行返回。
- 为了保证“停止请求”通信正确,此标记变量必须是 volatile 或 对其访问操作进行同步。
示例:假设你的程序中有以下三个方法 start、stop、run。
private Thread blinker; void start() { blinker = new Thread(this); blinker.start(); } void stop() { blinker.stop(); // 不安全的操作 } void run() { while (true) { try { Thread.sleep(interval); } catch (InterruptedException e) { } repaint(); } }
那么你可以改造自己的 stop 和 run,避免使用 Thread.stop:
private volatile Thread blinker; void stop() { blinker = null; } void run() { Thread thisThread = Thread.currentThread; while (blinker == thisThread) { // 每次迭代前先检查标记变量 try { Thread.sleep(interval); } catch (InterruptedException e) { repaint(); } } }
如何终止一个等待了很长时间的线程?(如,等待输入内容很久的线程)
Thread.interrupt 方法就是用来干这事的。
可以结合上述 “基于状态信号机制” 使用:状态变化后调用此方法,以中断等待。如:
void stop() { Thread moribund = waiter; waiter = null; moribund.interrupt(); }
这项技术中,非常关键的一点是,如果方法捕获到中断异常,但又不想立即处理它,那就得 “再次声明” 此异常。(因为 InterruptedException 是受检异常。)
之所以说是 “再次声明” 而不是 “再次抛出”,是因为并不是所有情况下都能再次抛出此异常。
如果方法捕获了 InterruptedException 且未声明会抛出此异常,那就需要 “重新中断自己”:
Thread.currentThread().interrupt();
这样就可以再次让线程抛出 InterruptedException。
如果线程没有响应 Thread.interrupt 该怎么办?
在某些情况下,你可以使用一些特定于应用程序的技巧。例,如果线程正在等待一个 socket,你可以将该 socket 关闭,从而让线程立即返回。
不幸的是,这方面真的没有通用的技术。
需要说明的是,在所有场景中,如果一个等待线程没有响应 Thread.interrupt,那么它也不会响应 Thread.stop。如,恶意的拒绝服务攻击(DoS),某些 stop/interrupt 无法正确工作的 IO操作 等。
为什么 Thread.suspend 和 Thread.resume 被废弃了?
Thread.suspend 容易引发死锁。
如果目标线程持有某项资源的锁,然后被挂起(suspend),那么在它被恢复前(resume)其它线程无法访问该资源。
此时,如果那个可以恢复目标线程的另一个线程需要 “先获取该资源的锁 - 再恢复目标线程”,那么就形成了死锁。
这种死锁通常表现为“冻结”进程。
我该用什么替代 Thread.suspend 和 Thread.resume ?
类似 Thread.stop,谨慎的做法是:
- 设置一个标记性变量来表示期望的线程状态;
- 目标线程轮询该变量;当期望状态是“挂起”时,线程通过 Object.wait 方法将自己挂起等待;
- 当期望目标线程继续运行时,通过 Object.notify 方法唤醒它。
例,你的程序中有一个事件处理方法 mousePressed 用于切换 blinker 线程的状态:
boolean threadSuspended; void mousePressed(MouseEvent e) { e.consume(); if (threadSuspended) { blinker.resume(); } else { blinker.suspened(); // 容易引发死锁 } threadSuspended = !threadSuspended; }
你可以改造为如下形式:
volatile boolean threadSuspended; synchronized void mousePressed(MouseEvent e) { e.consume(); threadSuspended = !threadSuspended; if (!threadSuspended) { notify(); } } void run() { while (true) { try { Thread.sleep(interval); if (threadSuspended) { synchronized(this) { while (threadSuspended) { wait(); } } } } catch (InterruptedException e) { } repaint(); } }
- run 方法就是 blinker 线程运行的内容。
- notify 和 wait 方法都被包在 synchronized 块中,保证它们的正确的执行顺序。这样可以避免因被挂起线程遗漏 notify 信号而导致无限挂起。
- 标记变量 threadSuspended 是 volatile,以保证挂起请求的正常通信。
- run 方法中的利用 双检锁 的形式来减少不必要的同步。
我能利用上述两个技术创建一个可以被 安全地 “stopped” 和 “suspended” 的线程吗?
可以,而且很简单。
比较微妙的是,另一个线程尝试停止目标线程时,目标线程可能已经处于挂起状态。
如果你的 stop 方法仅仅是更改 状态变量的值,那么目标线程是继续挂起(在monitor上等待),而不是期望的“优雅退出”。这会导致程序行为不稳定。
为了矫正这种状况,stop 方法必须确保目标线程能从挂起状态立即恢复。
目标线程一旦恢复,必须能立即识别出自己已被标记为“需要终止”。
实现示例:
private volatile Thread blinker; void run() { Thread thisThread = Thread.currentThread(); while (blinker == thisThread) { // 判断是否“需要终止” try { Thread.sleep(interval); synchronized(this) { // 只有当 不需要终止 且 期望挂起 时才会执行 wait while (threadSuspended && blinker==thisThread) { wait(); } } } catch (InterruptedException e) { } repaint(); } } synchronized void stop() { blinker = null; // 会被目标线程判定为“需要终止” notify(); // 立即唤醒目标线程 }
当然,你也可以在 stop 方法中调用 Thread.interrupt 来终止目标线程,这样就不需要调用 notify。
但 interrupt 方法也必须包含到 synchronized 中,确保目标线程不会错过 interrupt 信号。
Thread.destroy 如何?
Thread.destroy 从来都没有被实现过,且已经被废弃了。如果它被实现了,那么它会像 Thread.suspend 那样容易引发死锁。(事实上,它相当于 Thread.suspend,且没有后续的 Thread.resume)
为什么 Runtime.runFinalizersOnExit 被废弃了?
Runtime.runFinalizersOnExit 不安全。
它可能会导致存活对象被finalize,且同时有其它线程正在操作这些对象,导致不稳定的行为或死锁。
虽然可以通过 Class 实现来避免存活对象被finalize,但最大多数程序员都不会怎么设计。他们都会假设对象被finalize时肯定已经死了。
而且此方法是设置 JVM 的一个标记变量,它会影响到所有类。让所有类的设计者都增加避免过早被finalize的防御肯定是不切实际的。
相关推荐
在Java中,直接调用线程的`stop()`方法来终止线程已被废弃,因为这可能导致资源泄露、数据不一致等问题。取而代之的是,开发者通常采用以下几种策略: 1. **使用标志变量(Flag)** 2. **中断线程(Interrupt)** 3...
`Thread`类提供了一系列用于线程控制的方法,包括`suspend()`和`resume()`,尽管这些方法已被标记为已废弃,并不推荐在现代Java程序中使用。 ### `suspend()`与`resume()`方法 `suspend()`方法用于暂时停止一个...
然而,在现代Java版本中,`Thread.stop()`方法已被废弃,因为它可能导致数据不一致或资源泄露等问题。现在推荐的做法是在`run()`方法内通过设置一个标志位来控制线程的运行状态,从而达到优雅地终止线程的目的。 ``...
Java不推荐直接停止线程(如Thread.stop(),已废弃),因为这可能导致资源泄露。推荐使用interrupt()方法来中断线程,并在run()方法中检查isInterrupted()或interrupted()状态,以优雅地退出线程。 六、线程池 Java...
2. **线程的启动与终止**:start()方法用于启动线程,而join()、interrupt()、stop()(已废弃)等方法用于控制线程的执行和停止。特别地,不推荐直接使用stop()方法,因为这可能导致资源未释放的问题。 3. **线程...
- **死亡状态**:线程执行完毕或被异常终止。 3. **线程的优先级** - Java支持10个优先级,范围从1(`Thread.MIN_PRIORITY`)到10(`Thread.MAX_PRIORITY`),默认优先级为5。 - 通过`Thread.setPriority(int ...
`Thread`类是Java中用于创建线程的核心类,提供了大量的方法来管理和控制线程的行为。下面将详细介绍`Thread`类的一些重要成员变量和方法: 1. **成员变量**: - `MIN_PRIORITY`:表示线程的最小优先级。 - `NORM...
Java多线程编程安全退出线程方法介绍 Java多线程编程安全退出线程方法的重要性在于确保线程的资源正确释放,避免程序工作在不确定的状态下。以下是Java多线程编程安全退出线程方法的知识点: 1. Thread.stop()方法...
Java实验9主要关注多线程的设计与应用,涵盖了线程的基本概念、创建、管理与控制,以及线程同步和互斥。以下是对实验内容的详细解释: 1. **线程概念**: Java中的线程是程序执行的最小单位,一个进程中可以包含多...
启动线程只需调用start()方法,而停止线程则相对复杂,因为直接的stop()方法已废弃,为防止资源泄露,通常通过共享变量或者中断标志来通知线程停止。线程的暂停通常使用suspend()和resume(),但这些方法存在死锁风险...
因此,从JDK 1.1开始,`Thread.stop()`方法已经被废弃,并建议不再使用。 #### 二、使用`interrupt()`方法的安全方式 **1. 使用共享变量控制线程的运行** 在许多情况下,我们可以通过一个共享变量(通常是...
而run()方法是线程执行的主体,当线程被调度时会执行run()方法。直接调用run()不会启动新线程,而是在当前线程中执行。 4. **ThreadLocal**:ThreadLocal为每个线程提供了一个独立的变量副本,确保线程间数据隔离。...
在 Java 中,Thread.stop() 方法是一个被废弃的方法,不被推荐使用的原因是该方法太过于暴力,强行把执行到一半的线程终止,并且会立即释放这个线程所有的锁。这将破坏了线程中引用对象的一致性。 使用判断标志位的...
在当前Java中,stop()方法会释放线程所持有的所有锁,可能导致数据不一致,而destroy()方法从未实现,因此在进行线程状态控制时,应该使用其他方法,比如volatile关键字、Thread.interrupt()、wait/notify机制或使用...
- **常用线程方法**: - `wait()`, `notify()`, `notifyAll()`用于线程间的同步。 - `suspend()`, `resume()`, `sleep()`用于线程控制。 - **讨论**: - `suspend()`和`resume()`已废弃,推荐使用更安全的同步机制...
suspend和resume方法用于暂停和恢复线程的执行,但这两个方法已被废弃,因为它们可能导致死锁。yield方法是一个静态方法,当线程调用yield时,它暗示当前线程愿意让出CPU给同优先级的其他线程执行。 Thread类还提供...
在Java编程领域,多线程和高并发是面试中经常被问到的重要知识点,也是大型系统设计的关键技术。本文将围绕“Java面试多线程高并发相关回家技巧”这一主题,深入探讨相关概念、原理以及面试中可能遇到的问题,帮助你...
最初与`suspend()`方法配合使用,但`suspend()`方法已经被废弃,因为它可能导致死锁。 5. **`void setDaemon(boolean on)`**: - 用于设置线程是否为守护线程。 - 守护线程会在所有非守护线程结束时自动退出。 ...
- `suspend()` 和 `resume()`:挂起和恢复线程,由于可能导致死锁,这两个方法也被废弃。 二、线程的优先级 1. 线程调度策略 Java中的线程调度有两种策略:抢占式调度和合作式调度。Java采用抢占式调度,优先级...
resume()和suspend()方法分别用于恢复和暂停线程,但这两个方法已被废弃,因为它们可能会导致死锁,建议使用其他方式比如wait()和notify()来实现线程间的通信;stop()方法可以强制结束线程,但是也被废弃,因为它...