Java多线程Suspend与Resume方法为何被弃用的探究

首先,我们应该知道此二类用来实现线程的暂停和恢复,但是最终这两个方法被官方废弃了呢,以至于编写代码的时候都用删除线提示不推荐使用,虽然suspend方法是过期作废的方法,但研究过期作废的原因还是很有必要的

 方法Suspend带来的缺点1-锁独占
情况1:

        锁独占带来的方法阻塞,为了演示这一情况,我特意写了一个出现此情况的代码

import java.util.concurrent.TimeUnit;

public class SuspendAndResumeThread {
    public synchronized void print()  {
        System.out.println(Thread.currentThread().getName()+"\tprint!");
        try {
            TimeUnit.DAYS.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

}
class Test {
   @org.junit.jupiter.api.Test
    public void test() throws InterruptedException {
       final SuspendAndResumeThread t = new SuspendAndResumeThread();
       System.out.println("suspend和resume对线程锁独占的缺点");
       Thread t1 = new Thread(new Runnable() {
           @Override
           public void run() {
                t.print();
           }
       });
       t1.setName("t1");
       t1.start();
       Thread t2 = new Thread(new Runnable() {
           @Override
           public void run() {
               t.print();
           }
       });
       t2.setName("t2");
       t1.suspend();
       t2.start();
   }
}

        在这段代码中,我们定义了SuspendAndResumeThread类,在其中定义了一个同步方法print,输出当前线程,并做延时操作,用来模拟线程一直占用锁的情况

        然后在测试方法中,我们定义并开启了两个线程,t1和t2,我们使t1和t2相对的同时去调用print方法,但是通过相应代码顺序的调整,确保t1要尽可能先于t2进入同步方法中,并占用锁,在这种情况下,我们将t1线程进行暂停,然后开启t2线程,通过以下输出结果

 

 可以发现,t2无法进入print线程,由此可知,如果Suspend方法使用不当,极易造成公共同步对象锁被独占,其他线程无法访问公共对象的结果

情况2:

        锁独占带来的线程无法终止,下面我将再编写一个此情况

class Test2 {
  private int i = 0;
    public void increment() {
        while (true){
            i++;
            System.out.println(Thread.currentThread().getName()+"\t"+i);
        }

    }

    Thread t = new Thread(new Runnable() {
        @Override
        public void run() {
            increment();
        }
    });

    @org.junit.jupiter.api.Test
    public void test() throws InterruptedException {
        t.start();
        TimeUnit.SECONDS.sleep(1);
        t.suspend();
        System.out.println("main end");
    }
}

通过输出我们可以发现 

main end是不会输出的,IDE也是一直未终止

那么为什么会出现这种情况呢?这里我们需要看一下println的源码

    public void println(String x) {
        if (getClass() == PrintStream.class) {
            writeln(String.valueOf(x));
        } else {
            synchronized (this) {
                print(x);
                newLine();
            }
        }
    }

在源码中,可以看到首先它检查调用println的方法是否是PrintStream类本身,如果是,则调用私有方法writeln(String x)执行输出操作

        若不是,则进入else分支,在这里它使用synchronized来确保线程安全的执行操作,在同步块中,首先调用print(x)方法对给定字符输出到标准输出,然后调用newLine进行换行,这种设计确保了多个线程同时调用 println 方法时的线程安全性,因为只有一个线程能够进入同步块并执行输出操作,其他线程需要等待当前线程执行完毕并释放锁资源后才能继续执行。

        回到现在的问题来看一切清晰明朗,t线程拿到了println的锁对象,但是它暂停了,它无法释放自己拥有的锁,而main线程想要输出需要获得t拥有的println锁对象,于是死锁产生了,这样mian线程就会永远等待t,IDE也会一直转圈圈

 方法Suspend带来的缺点2-数据不完整

        这里,我写了一段代码用来演示这种情况

class Test3 {
    private String name = "zhangsan";
    private String password = "123456";
    public void setData(String name,String password){
        this.name = name;
        if(Thread.currentThread().getName().equals("t")){
            System.out.println("t线程暂停");
            Thread.currentThread().suspend();
        }
        this.password = password;
    }

   public void getPassword(){
        System.out.println(this.name+"\t"+this.password);
    }

    @org.junit.jupiter.api.Test
    public void test() throws InterruptedException {
        final Test3 test3 = new Test3();
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                test3.setData("lisi","000000");
            }
        });
        t.setName("t");
        t.start();
        Thread.sleep(2000);
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                test3.getPassword();
            }
        });
        t2.setName("t2");
        t2.start();

    }
}

通过输出可见

数据的修改出现了错误的情况,数据变得不完整,同样的这种情况让我想到之前模拟的那种多线程的错误情况,A和B同时修改一个实例类中的username和password属性,结果A在修改用户名后线程模拟休眠4s,此时B线程也来修改这一实例,当B将用户名和密码都修改完成后,A线程醒来修改实例密码,那么就会出现A的用户名B的密码的线程安全带来的错误问题,由此可见,Suspend方法确实在数据的一致性方面也有很大的欠缺。

        综上所述,Suspend线程暂停方法与Resume恢复线程方法被淘汰确实是时代的选择

  • 24
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值