挂起(suspend)与线程阻塞工具类LockSupport

Posted luozhiyun

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了挂起(suspend)与线程阻塞工具类LockSupport相关的知识,希望对你有一定的参考价值。

挂起(suspend)与线程阻塞工具类LockSupport

一般来说是不推荐使用suspend去挂起线程的,因为suspend在导致线程暂停的同时,并不会去释放任何锁资源. 如果其他任何线程想要访问被它暂用的锁时,都会被牵连,导致无法正常继续运行. 直到对应的线程上进行了resume操作.

并且,如果resume操作意外的在suspend前执行了,那么被挂起的线程可能很难有机会被继续执行,更严重的是:它所占用的锁不会被释放,因此可能会导致整个系统工作不正常,而且,对于被挂起的线程,从它的线程状态上看,居然还是Runnable

/**
 * @author luozhiyun on 2018/6/24.
 */
public class BadSuspend {

    public 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 " + getName());
                Thread.currentThread().suspend();
            }
        }
    }

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

执行后我们可能会得到以下输出:

in t1
in t2

这表明两个线程先后进入了临界区,但是程序不会退出

线程阻塞类:LockSupport

它可以在线程内任意位置让线程阻塞. 和Thread.suspend()相比,它弥补了由于resume()在前发生,导致线程无法继续执行的情况.和Object.wait()相比,它不需要先获得某个对象的锁,也不会抛出InterruptedException

LockSupport的静态方法park()可以阻塞当前线程,类似的还有parkNanos() / parkUntil()等方法.它们实现了一个限时的等待

/**
 * @author luozhiyun on 2018/6/24.
 */
public class LockSupportDemo {

    public 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 " + getName());
                LockSupport.park(this);
            }
        }
    }

    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();
    }
}

这段代码至始至终都可以正常的结束,不会因为park()方法而导致线程永久性的挂起

这事因为LockSupport类使用类似信号量的机制.它为每一个线程准备了一个许可,如果许可可用,那么park()函数会立即返回,并且消费这个许可(也就是将许可变成不可用).如果许可不可用,就会阻塞.而unpark()则使得一个许可变为可用(但是和信号量不同的是,许可不能累加,你不可能拥有超过一个许可,它永远只有一个)

这个特点使得:即使unpark()操作发生在park()之前,它也可以使下一次的park()操作立即返回

同时,处于park()挂起状态的线程不会像suspend()那样还给出一个令人费解的Runnable的状态.它会非常明确地给出一个WAITING状态,甚至还会是标注是park()引起的

以上是关于挂起(suspend)与线程阻塞工具类LockSupport的主要内容,如果未能解决你的问题,请参考以下文章

Java并发:挂起与唤醒线程LockSupport工具类详解

Kotlin -- 知识点协程

Kotlin 协程协程的挂起和恢复 ② ( 协程挂起 和 线程阻塞 对比 )

Kotlin 协程协程的挂起和恢复 ② ( 协程挂起 和 线程阻塞 对比 )

java 多线程7: (suspend方法与resume方法) 挂起与恢复

java线程