为什么 JDK 1.1 版本后不推荐使用 Thread.stop()、Thread.suspend() 和 Thread.resume() 方法?
Thread 类包含用于创建和操作线程的构造函数和方法。 Thread 是实现 Runnable 接口的 Object 的子类。线程类中有许多方法,但其中一些方法从 JDK 1.1 开始已被弃用。在本文中,我们将了解其背后的原因。
弃用的方法是那些不再被认为重要且不应使用的方法,因为它们可能会从它们的类中删除。类随着时间的推移而演变,导致它们的 API 发生变化,从而导致弃用。属性发生变化,方法被重命名,并添加了新的方法。为了帮助开发人员从旧 API 转移到新 API,已弃用的类和方法在文档注释中标记为 @deprecated。
为什么弃用??
由于其固有的风险, Thread.stop()正在被淘汰。当您停止一个线程时,它会解锁它锁定的所有监视器。如果以前受这些监视器保护的任何对象处于不一致状态,则其他线程可能会看到这些对象处于不一致状态。
作用在损坏物体上的线程可能有意识或无意识地行为不规律。 ThreadDeath 与其他不受控制的异常不同,它会默默地杀死线程,让用户不会收到程序可能已损坏的警告。损坏发生后,损坏可能会在不可预测的时刻出现。此外,在多线程环境中使用 DBMS – JDBC 时,杀死线程会产生问题。
Thread.suspend()已被弃用,因为它本质上容易死锁。因此,也必须弃用Thread.resume() 。当目标线程被挂起时,它在监视器上持有一个锁来保护一个关键的系统资源,在目标线程恢复之前,没有其他线程可以访问它。如果将重新启动目标线程的线程在调用 resume() 之前尝试锁定此监视器,则会发生死锁。
使用这些不推荐使用的方法可能导致死锁的示例:
Java
// This class contains an integer array &
// Threads set the element's value for this array
class NumVal {
private int num[] = null;
boolean valueSet = false;
int i = 0;
NumVal()
{
// Creating integer array of 10 elements
num = new int[10];
}
// method to set the values in the array
public void setVal(int n)
{
if (i < 9) {
System.out.println("Putting value " + n
+ " in the NumVal Array");
num[i] = n;
i++;
}
}
// method to get the values from the array
public int getVal()
{
if (i >= 0) {
System.out.println("Giving n = " + num[i]);
i--;
return num[i + 1];
}
else {
return -1;
}
}
}
// Creating Our Thread Class
class MyThread extends Thread {
// MyThread want mutually exclusive
// lock on the object
// referred by: NumObjToSetVal
NumVal NumObjToSetVal = null;
// Constructor
public MyThread(String threadName, NumVal numV)
{
super(threadName);
NumObjToSetVal = numV;
}
public void run()
{
// Only 1 thread at a time an access the object
// referred by : NumObjToSetVal
synchronized (NumObjToSetVal)
{
int n = 0;
while (n < 5) {
System.out.println(
"THREAD NAME : "
+ Thread.currentThread().getName());
n++;
NumObjToSetVal.setVal(n);
try {
// Make the thread sleep for 100 ms
Thread.sleep(100);
System.out.println(
Thread.currentThread().getName()
+ "is awake now");
}
catch (Exception e) {
System.out.println("Exception Caught");
}
// If n is 2 , we suspend this thread
if (n == 2) {
// suspend the thread, now this thread
// will release lock on NumObjToSetVal
// only when resume() method is called
// on this thread, thread will go in
// waiting state
Thread.currentThread().suspend();
}
}
}
}
}
public class Main {
public static void main(String[] args)
{
// TODO Auto-generated method stub
NumVal v = new NumVal();
// Creating thread 1 that want exclusive lock on
// object referred by v
MyThread thread1 = new MyThread("Thread1 ", v);
// Creating thread 2 that want exclusive lock on
// object referred by v
// thread1 is not going to release lock on Object
// referred by v until resume() method is not called
// and for acquiring lock on v Object refred by v ,
// thread1 must have released lock on Object
// referred by v, if lock is not released, thread2
// will keep on waiting for thread1 to release lock
// onbject referred by v & deadlock will be formed
MyThread thread2 = new MyThread("Thread2 ", v);
// starting both threads
thread1.start();
thread2.start();
for (int i = 500; i <= 501; i++) {
System.out.println("Main Thread " + i);
}
}
}
输出:
THREAD NAME : Thread1
Putting value 1 in the NumVal Array
Main Thread 500
Main Thread 501
Thread1 is awake now
THREAD NAME : Thread1
Putting value 2 in the NumVal Array
Thread1 is awake now
//Deadlock is created & hence no output after this
说明:我们创建了 2 个线程:thread1 和 thread2。两者都希望获得对 'v' 引用所引用的 NumVal 对象的锁定。
当我们通过调用 start() 方法启动两个线程时,只要线程获得 CPU,run() 方法就会执行。线程 1 获取 CPU,当其 run 方法中 n 的值为 2 时,线程被挂起。在调用 resume() 之前,Thread1 不会释放对 'v' 引用的对象的锁定。
要使线程 2 获得对由 'v' 引用的 v 对象的锁,线程 1 必须释放对由 'v' 引用的对象的锁。这里的锁没有被释放,线程 2 将继续等待线程 1 释放对 'v' 引用的对象的锁,从而形成死锁。
因此,每当您在同一线程上调用 suspend() 方法时,您总是(在任何时间)在线程上调用 resume()。