1 概述

在Java并发中可以使用suspend方法和resume方法分别来暂停和恢复线程的执行。虽然这两个已经被弃用了,但是有必要探究一下它们为什么被抛弃。

2 使用样例

参考下面的简单的案例和结果的截图可以大致了解这两个方法的使用方法,这里不多做解释。

/**
 * Created by fubinhe on 16/9/28.
 */
public class SuspendAndResumeDemo {
    public static void main(String[] args) {
        try {
            TestThread t = new TestThread();
            t.start();
            Thread.sleep(1000);
            t.suspend();
            System.out.println("1: cnt=" + t.getCnt() + "------" + System.currentTimeMillis() / 1000);
            Thread.sleep(2000);
            System.out.println("2: cnt=" + t.getCnt() + "------" + System.currentTimeMillis() / 1000);
            t.resume();
            Thread.sleep(1000);
            System.out.println("3: cnt=" + t.getCnt() + "------" + System.currentTimeMillis() / 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
 
class TestThread extends Thread {
 
    private int cnt = 0;
 
    public int getCnt() {
        return cnt;
    }
 
    public void setCnt(int cnt) {
        this.cnt = cnt;
    }
 
    @Override
    public void run() {
        while (true) {
            ++cnt;
        }
    }
}

3 被抛弃的原因

3.1 锁独占

supsend和resume方法如果使用不当,很容易造成同步对象即锁的被独占,使得其他的线程无法正常的访问到同步对象,参考以下代码:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
 
/**
 * Created by fubinhe on 16/9/28.
 */
public class SuspendAndResumeDemo {
    public static void main(String[] args) {
        ExecutorService exec = Executors.newCachedThreadPool();
        final Object lock = new Object();
        for (int i = 0; i < 2; ++i) {
            exec.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + " begins to run");
                    synchronized (lock) {
                        System.out.println("lock catched by " + Thread.currentThread().getName() + " forever");
                        Thread.currentThread().suspend();
                    }
                    System.out.println(Thread.currentThread().getName() + " ends");
                }
            });
        }
    }
}

运行结果如下图所示,可以看出锁被第一个线程拿到后,执行了supsend方法,然后锁就一直被它独占,使得第二个线程无法拿到锁进入同步代码块,而只打印了同步代码块外面的输出语句。

3.2 一不小心就会踩到的“坑”

关于锁独占还有一个常见的一不留神就会踩到的“坑”,参考下面的代码:

/**
 * Created by fubinhe on 16/9/28.
 */
public class SuspendAndResumeDemo {
    public static void main(String[] args) {
        try {
            Thread t = new Thread(new Runnable() {
 
                private long cnt = 0;
 
                @Override
                public void run() {
                    while (true) {
                        System.out.println(cnt++);
                    }
                }
            });
            t.start();
            Thread.sleep(10);
            t.suspend();
            System.out.println("main ends");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public final static PrintStream out = null,可以看出这个对象是final修饰的(关于这个对象的赋值是在initializeSystemClass方法中,具体可以参考System类的源代码),所以在每个线程中的这个this锁是一个锁。而子线程从开始就拿到这个锁,执行suspend方法后,这个锁一直被它独占了,这也就是为什么主线程拿不到这个锁从而执行不到最后的打印方法。

                


3.3 不同步

这两个方法还有一个缺点就是很容易会造成数据的不同步,可以参考下面的代码和运行结果进行理解。

/**
 * Created by fubinhe on 16/9/28.
 */
public class SuspendAndResumeDemo {
    public static void main(String[] args) {
        try {
            final User user = new User("111", "aaa");
            new Thread(new Runnable() {
                @Override
                public void run() {
                    user.setId("222");
                    Thread.currentThread().suspend();
                    user.setName("bbb");
                }
            }).start();
            Thread.sleep(10);
            System.out.println("id: " + user.getId() + ", name: " + user.getName());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
 
class User {
 
    private String id;
 
    private String name;
 
    public User(String id, String name) {
        this.id = id;
        this.name = name;
    }
 
    public String getId() {
        return id;
    }
 
    public void setId(String id) {
        this.id = id;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
}




综合以上所说的几点,就可以理解为什么这两个方法会被Java抛弃了。