聊聊线程阻塞与恢复 LockSupport类

Java · andrew · 2019-01-11 · 1 次阅读

文章来源: https://juejin.im/post/5c32f5fcf265da613b6fef0e, 本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。

最近在学习 Java 并发包里面各种锁的实现以及AQS框架时,了解到线程阻塞都是通过 LockSupport 这样一个类来实现的。查阅了相关资料发现早期的线程阻塞方式是直接借助 Thread 类的 suspend 方法来完成。然而,现在使用 suspend 以及 resume 方法都会被提醒方法已过期,且容易导致死锁。下面就里面的原因进行简单的探究。

一个Suspend方法导致死锁的经典案例

public class SuspendTest {

    public static void main(String args[]) throws InterruptedException {
        Thread t = new Thread(new MyTask());
        t.start();
        Thread.sleep(100);
        t.suspend();
        System.out.println("resuming");
        t.resume();
        Thread.sleep(100);
        t.interrupt();
    }

    public static class MyTask implements Runnable {

        @Override
        public void run() {
            int count = 0;
            while (!Thread.currentThread().isInterrupted()) {
                count++;
                System.out.println("looping: " + count);
        }
    }
}
复制代码

上述代码在执行的过程中很有可能会在 System.out.println("resuming") 这句语句处进入死锁状态。这是因为 println 方法是一个同步方法,在执行 t.suspend() 时,线程t很有可能已经获取out对象的锁,进入了 println 方法。从而导致主线程获取不到阻塞的t线程占据的锁,进而发生死锁。

使用 suspend 方法带来的不确定性正是因为线程t的阻塞是由外界控制的,也就意味t阻塞的时候执行的代码位置、数据状态、锁的信息都是不能确定的。

public static class MyTask implements Runnable {

    @Override
    public void run() {
        boolean state = true;
        int count = 0;
        while (state && !Thread.currentThread().isInterrupted()) {
            count++;
            System.out.println("looping: " + count);
            try {
                Thread.sleep(10);
            } catch (InterruptedException ex) {
                System.out.println(ex);
                state = false;
            }
        }
    }
}
复制代码

我们给任务线程t的 println 方法后面加上一段睡眠时间,这样t被阻塞时可能正在打印内容,也有可能在睡眠。主线程的 println 方法就有可能获取到锁,从而顺利执行下去。改变睡眠的时间,程序是否会发生死锁的概率也会改变。

LockSupport类

我们可以使用 LockSupport 类来实现线程的阻塞与恢复,相关的方法是 parkunpark

public static void park(Object blocker) {
    Thread t = Thread.currentThread();
    setBlocker(t, blocker);
    UNSAFE.park(false, 0L);
    setBlocker(t, null);
}

public static void unpark(Thread thread) {
    if (thread != null)
        UNSAFE.unpark(thread);
}
复制代码

park 方法可以接收一个阻塞块对象(许可),并关联到阻塞的线程上。 unpark 的参数为需要解除阻塞的线程。我们可以看到 LockSupport 只能够阻塞当前线程,也就意味着线程在何处进入阻塞状态是由自己决定的。下面通过这种方式实现前面的打印程序。

public class SuspendTest {

    public static void main(String args[]) throws InterruptedException {
        Thread t = new Thread(new MyTask());
        t.start();
        Thread.sleep(100);
        System.out.println("resuming");
        LockSupport.unlock(t);
        Thread.sleep(100);
        t.interrupt();
    }

    public static class MyTask implements Runnable {

        @Override
        public void run() {
            int count = 0;
            while (!Thread.currentThread().isInterrupted()) {
                count++;
                System.out.println("looping: " + count);
                LockSupport.lock(this);
        }
    }
}
复制代码

上述代码没有死锁问题,t线程进入阻塞状态是在 println 执行之后。相比 suspend 方式缺少一定的灵活性,但是线程状态更加具有确定性。线程没有模糊状态,那么死锁的发生就可能是由程序本身的设计带来的,比如我们在线程释放相关锁之前执行 LockSupport.lock() 进入阻塞状态,如下所示,那么死锁就一定会发生。

public class SuspendTest {

    public static ReentrantLock lock = new ReentrantLock();

    public static void main(String args[]) throws InterruptedException {
        Thread t = new Thread(new MyTask());
        t.start();
        Thread.sleep(100);
        lock.lock();
        System.out.println("resuming");
        lock.unlock();
        LockSupport.unlock(t);
        Thread.sleep(100);
        t.interrupt();
    }

    public static class MyTask implements Runnable {

        @Override
        public void run() {
            int count = 0;
            while (!Thread.currentThread().isInterrupted()) {
                count++;
                lock.lock();
                System.out.println("looping: " + count);
                LockSupport.lock(this);
                lock.unlock();
        }
    }
}
复制代码

以上所述就是小编给大家介绍的《聊聊线程阻塞与恢复 LockSupport类》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

码农书籍
Python源码剖析:深度探索动态语言核心技术

Python源码剖析:深度探索动态语言核心技术

陈儒 / 电子工业出版社 / 2008-6 / 69.80元

作为主流的动态语言,Python不仅简单易学、移植性好,而且拥有强大丰富的库的支持。此外,Python强大的可扩展性,让开发人员既可以非常容易地利用C/C++编写Python的扩展模块,还能将Python嵌入到C/C++程序中,为自己的系统添加动态扩展和动态编程的能力。.为了更好地利用Python语言,无论是使用Python语言本身,还是将Python与C/C++交互使用,深刻理解Python的运行原理都是非常重要的。本书以CPython为研究对象,在C代码一级,深入细致地剖析了Python的实现。书中不仅包括了对大量Python内置对象的剖析,更将大量的篇幅用于对Python虚拟机及Python高级特性的剖析。通过此书,读者能够透彻地理解Python中的一般表达式、控制结构、异常机制、类机制、多线程机制、模块的动态加载机制、内存管理机制等核心技术...

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

随机密码生成器
随机密码生成器

多种字符组合密码

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试