Thread的suspend()、resume()与LockSupport的park()、unpark()

Thread的suspend()、resume()方法

suspend()方法挂起线程、resume()方法继续执行,如果一个线程调用suspend()方法后,可以通过resume()继续线程的执行。但是jdk已将这俩方法标位@Deprecated,因为使用suspend()方法在使线程暂停但并不会释放线程占用的资源,直到对应线程调用了resume()方法。被挂起的线程才可以继续执行。从而阻塞在临界资源的线程也能执行。但是,如果resume()方法操作意外地在suspend()方法前就执行了,那么被挂起的线程可能很难有机会被继续执行。并且,更严重的是:它所占用的锁不会被释放,因此可能会导致整个系统工作不正常。而且,对于被挂起的线程,从它的线程状态上看,居然还是Runnable,这也会严重影响我们对系统当前状态的判断。比如如下代码

public class BadSuspend {

    public static Object u = new Object();

    static COThread t1 = new COThread("t1");
    static COThread t2 = new COThread("t2");
    static class COThread extends Thread{
        public COThread(String name){
            super.setName(name);
        }
        @Override
        public void run() {
            synchronized (u){
                System.out.println("In"+getName());
                //该方法不会释放锁
                Thread.currentThread().suspend();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        t1.start();
        Thread.sleep(1000);
        t2.start();
        t1.resume();
        t2.resume();
        t1.join();
        t2.join();
    }
}

执行后控制台输出

int1
int2

BadSuspend.java中线程t1和线程t2进入了临界区,但程序不会退出而是挂起。通过jvisualvm对当前运行程序进行线程dump看到t2的状态为RUNNABLE

Thread的suspend()、resume()与LockSupport的park()、unpark()_第1张图片

 

这是因为;在当前系统线程t2其实被挂起了,所以他的状态为RUNNABLE。我们很容易对当前系统造成误判。在上述代码中,,虽然主函数中已经调用了resume()方法,但是由于时间先后顺序的缘故,那个resume并没有生效!这就导致了线程t2被永远挂起,并且永远占用了对象u的锁。这对于系统来说极有可能是致命的。

 

LockSupport的park()、unpark()方法

LockSupport.park()、LockSupport.unpark()

resume()方法。它弥补了resume()、suspend()方法的不足。并且与Object的wait()方法相比,它也不需要先获得某个对象的锁也不会抛出InterruptedException。我们将BadSuspend.java的suspend()替换为LockSupport.park(),resume()方法替换为LockSupport,unpark(),再次运行,程序始终可以正常结束。这是由于LockSupport很类似于二元信号量(只有1个许可证可供使用),如果这个许可还没有被占用,当前线程获取许可并继续执行;如果许可已经被占用,当前线程阻塞,等待获取许可。

比如如下代码会一直导致主线程阻塞

public class LockSupportTest {
    public static void main(String[] args) {
        LockSupport.park();
    }
}

这是因为在许可在默认情况下是被占用的,所以上述程序进入阻塞状态。通过jvisualvm查看到main线程的状态如下

Thread的suspend()、resume()与LockSupport的park()、unpark()_第2张图片

 

如果我们先调用LockSupport.unpark()将许可释放,在调用LockSupport.park()程序就能正常结束,一般情况下多次调用unpark()方法,只调用一次park()方法程序也没什么问题。

public class LockSupportTest {
    public static void main(String[] args) {
        LockSupport.unpark(Thread.currentThread());
        LockSupport.park();
    }
}

注意LockSupport是不可重入的,如果一个线程连续两次调用LockSupport.park()那么该线程会一直阻塞下去。

LockPark.park()支持线程中断影响。

LockSupport.park()方法不会抛出InterruptedException异常。它只会默默返回,但是我们可以从Thread.interrupted()等方法中获得中断标记。

public class LockSupportIntDemo {
    public 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.setName(name);
        }

        @Override
        public void run() {
            synchronized (u) {
                System.out.println("in" + getName());
                LockSupport.park();
                if (Thread.interrupted()) {
                    System.out.println(getName() + "被中断了");
                }
                System.out.println(getName() + "执行结束了");
            }
        }
    }

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

注意,上述代码在第27行中断了处于park()方法状态的t1。之后,t1可以马上响应这个中断,,并且返回。t1返回后在外面等待的t2才可以进入临界区,并最终由LockSupport.unpark(t2)操作使其运行结束。

Thread的suspend()、resume()与LockSupport的park()、unpark()_第3张图片

参考:https://blog.csdn.net/shijiejiujiuba/article/details/79034307

你可能感兴趣的:(Java杂记)