关于线成暂停和恢复的那些事儿

关于线成暂停和恢复的那些事儿

suspend()和resume()

这是一对相反的操作,suspend可以挂起当前线成,而resume可以恢复当前线成。 乍一看这对操作非常有用,但是如果对jdk源码稍微有点了解,会发现这两个方法早就废弃了。

为什么会废弃呢?

因为如果resume操作,出现在suspend之前的话,线成会永久阻塞,会影响到后面访问当前临界区的所有线成。最误导人的是,当前线成的状态是runnable。

suspend问题演示

package com.allen.dayup.高并发程序设计.chap1;

/**
 * @Auther: allen
 * @Date: 2020-03-19 21:16
 * @Description:
 */
public class BadSuspend {

    static Object u = new Object();

    static ChangeObjectThread t1 = new ChangeObjectThread("t1");
    static ChangeObjectThread t2 = new ChangeObjectThread("t2");

    public static class ChangeObjectThread extends Thread{

        public ChangeObjectThread(String name) {
            super.setName(name);
        }

        @Override
        public void run() {
            synchronized (u){

                System.out.println("in " + Thread.currentThread().getName());
                Thread.currentThread().suspend();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException{
        //对照观察t1和t2,
        //休眠主线程让t1阻塞:t1线成是先调用suspend,然后再resume
        //主线程启动t2后直接调用resume:t2线程的resume会在suspend之前调用

        t1.start();
        //主线程休眠100毫秒,保证t1已经阻塞
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t2.start();
        t1.resume();
        t2.resume();
        
        //等待t1和t2执行完成后,主线程再继续执行
        t1.join();
        t2.join();
    }
}


运行程序会发现主线程会一直阻塞,用jstack查看线成信息发现t2线成是runnable状态

C:\Users\allen>jstack 20336
2020-04-18 09:11:31
Full thread dump OpenJDK 64-Bit Server VM (25.161-b14 mixed mode):

"t2" #15 prio=5 os_prio=0 tid=0x000000001f4a60f0 nid=0x42f0 runnable [0x00000000203af000]
   java.lang.Thread.State: RUNNABLE
        at java.lang.Thread.suspend0(Native Method)
        at java.lang.Thread.suspend(Thread.java:1032)
        at com.allen.dayup.高并发程序设计.chap1.BadSuspend$ChangeObjectThread.run(BadSuspend.java:26)
        - locked <0x000000076b8d2618> (a java.lang.Object)

"Service Thread" #13 daemon prio=9 os_prio=0 tid=0x000000001f387510 nid=0x56c0 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C1 CompilerThread3" #12 daemon prio=9 os_prio=2 tid=0x000000001f2efab0 nid=0x34a8 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread2" #11 daemon prio=9 os_prio=2 tid=0x000000001f2ef630 nid=0x256c waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread1" #10 daemon prio=9 os_prio=2 tid=0x000000001f2ef1b0 nid=0x4c90 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread0" #9 daemon prio=9 os_prio=2 tid=0x00000000035164f0 nid=0x5530 waiting on condition [0x0000000000000000]

利用wait和notify,用一个钩子方法来阻塞和恢复线成

public class GoodSuspend {

    static Object lock = new Object();

    static class ChangeObjectThread extends Thread {

        volatile boolean suspendMe = false;

        public void suspendMe(){
            this.suspendMe = true;
        }

        public void resumeMe(){
            suspendMe = false;
            synchronized (this){

                notify();
            }
        }

        @Override
        public void run() {

            while (true){
                //如果suspendMe为true,调用wait方法阻塞当前线成;否则,就执行业务逻辑
                synchronized (this) {
                    while (suspendMe) {
                        try {
                            wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }


                    synchronized (lock) {
                        System.out.println("in ChangeObjectThread!");
                    }

                    Thread.yield();
                }
            }
        }
    }

    static class ReadObjectThread extends Thread{

        @Override
        public void run() {
            while (true){
                synchronized (lock){
                    System.out.println("in ReadObjectLock!");
                    Thread.yield();
                }
            }
        }
    }

    public static void main(String[] args) throws InterruptedException{
        ChangeObjectThread t1 = new ChangeObjectThread();
        ReadObjectThread t2 = new ReadObjectThread();
        t1.start();
        t2.start();
        Thread.sleep(1000);
        t1.suspendMe();
        System.out.println("=====================》suspend t1 2 second");
        Thread.sleep(2000);
        System.out.println("resume t1");
        t1.resumeMe();
    }
}

  1. 启动t1和t2线成,t1和t2交替输出一秒。
  2. 然后阻塞t1两秒,就只有t2线成输出
  3. 最后恢复t1,又重新交替输出

park()和unpark()

不过对于线成暂停和恢复,再jdk的并发包中提供了LockSupport的处理。

  • park():阻塞线程
  • unpark():恢复线程

在park实现中,阻塞线成之前先去请求一个许可,请求到才能阻塞线成。只有线成unpark后,许可才会变成可用状态。

所以就算unpark发生在park之前,也不会导致线成永久挂起。

public class LockSupportDemo {
    static Object u = new Object();
    static ChangeObjectThread t1 = new ChangeObjectThread("t1");
    static ChangeObjectThread t2 = new ChangeObjectThread("t2");

    static class ChangeObjectThread extends Thread {

        public ChangeObjectThread(String name) {
            super(name);
        }

        @Override
        public void run() {
            synchronized (u){
                System.out.println("in " + getName());
                LockSupport.park();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        t1.start();
        Thread.sleep(100);
        t2.start();
        LockSupport.unpark(t1);
        LockSupport.unpark(t2);
        t1.join();
        t2.join();

    }
}

这段代码只是将suspend和resume,替换成了park和unpark。执行结果会发现线成正常运行结束。

参考文档

实战java高并发程序 葛一鸣

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值