教你如何使用Java多线程编程LockSupport工具类

 更新时间:2021年04月15日 09:40:39   作者:Tony-devj  
在Java工具包中有一个LockSupport工具类,主要负责挂起和唤醒线程,这篇文章主要介绍了教你如何使用Java多线程编程LockSupport工具类,需要的朋友可以参考下

LockSupport类

用于创建锁和其他同步类的基本线程阻塞原语,此类与使用它的每个线程关联一个许可。如果获得许可,将立即返回对park的调用,并在此过程中消耗掉它;否则may会被阻止。调用unpark可使许可证可用(如果尚不可用)。(不过与信号量不同,许可证不会累积。最多只能有一个。)
方法park和unpark提供了有效的阻塞和解阻塞线程的方法,这些线程不会遇到导致已弃用的方法Thread.suspend和Thread.resume无法用于以下问题:由于许可,在调用park的一个线程与试图进行unpark的另一个线程之间的竞争将保留生命力。此外,如果调用者的线程被中断并且支持超时版本,则park将返回。 park方法也可能在其他任何时间出于“无理由”返回,因此通常必须在循环中调用该循环,该循环在返回时会重新检查条件。从这个意义上说,park是对“繁忙等待”的优化,它不会浪费太多的时间,而必须与unpark配对才能生效。
了解了其作用,我们看看相关API。

LockSupport.park()

park():除非有许可,否则出于线程调度目的禁用当前线程。
如果许可证可用,则将其消耗掉,并立即返回呼叫;否则,将立即返回该呼叫。出于线程调度的目的,当前线程将被禁用,并处于休眠状态,直到发生以下三种情况之一:
1.其他一些线程以当前线程为目标调用unpark()方法
2.其他一些线程Threadinterrupt interrupts当前线程
3.虚假地调用返回
源码:

public static void park() {
    UNSAFE.park(false, 0L);
}

LockSupport都是调用的UNSAFE的方法,我们先看看park方法

public class LockSupportMain {
    public static void main(String[] args) {
        System.out.println("begin park!");
        LockSupport.park();
        System.out.println("end park!");
    }
}

运行结果:

在这里插入图片描述

我们调用了park方法,导致Main线程一直被阻塞,一直没有结束,因为默认的情况下,调用线程是不持有许可证的,我们如何解决呢?上面提到过三个方式,我们一个一个的验证。

1.调用unpack方法获得许可

unpack():如果给定线程尚不可用,则使它可用。如果线程在park上被阻止,则它将取消阻止。否则,将确保其对park的下一次调用不会阻塞。如果给定线程尚未启动,则不能保证此操作完全无效。
源码:

public static void unpark(Thread thread) {
    if (thread != null)
        UNSAFE.unpark(thread);
}
public class LockSupportMain2 {
    public static void main(String[] args) {
        System.out.println("begin park!");
        LockSupport.unpark(Thread.currentThread());
        LockSupport.park();
        System.out.println("end park!");
    }
}

运行结果:

在这里插入图片描述

可以看出,当前的线程main已经释放了,没有进行阻塞,直接运行完成了。
我们创建一个线程有Main线程进行unpark方法将线程在阻塞的情况下,进行运行。

public class LockSupportMain3 {

    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("begin start thread name: " + Thread.currentThread().getName() + " park");
                LockSupport.park();
                System.out.println("end start thread name: " + Thread.currentThread().getName() + " park");
            }
        };

        Thread thread = new Thread(runnable);
        thread.start();
        Thread.sleep(2000);
        System.out.println("main thread call unpark");
        LockSupport.unpark(thread);
    }

}

运行结果:

在这里插入图片描述

运行结果可以看出,其他一些线程以当前线程为目标调用unpark()方法可以将线程的park导致阻塞的继续运行。

2.调用中断interrupts方法获得许可

由于park方法不会告诉你何种原因返回,所以调用者需要根据之前调用park方法的原因,再次检查条件是否满足,如果不能满足,就还需要调用park方法

public class LockSupportMain4 {

    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("begin start thread name: " + Thread.currentThread().getName() + " park");
                while (!Thread.currentThread().isInterrupted()) {
                    LockSupport.park();
                }
                System.out.println("end start thread name: " + Thread.currentThread().getName() + " park");
            }
        };

        Thread thread = new Thread(runnable);
        thread.start();
        Thread.sleep(2000);
        //通过interrupt方法 让park阻塞继续运行
        thread.interrupt();
    }

}

运行结果:

在这里插入图片描述

由运行可以看出,其他一些线程Threadinterrupt interrupts当前线程是可以将park阻塞的线程继续运行。

parkNanos(long nanos)

parkNanos(long nanos):除非允许使用许可,否则在指定的等待时间内禁用用于线程调度的当前线程。
源码:

public static void parkNanos(long nanos) {
    if (nanos > 0)
        UNSAFE.park(false, nanos);
}
public class LockSupportMain5 {

    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("begin start thread name: " + Thread.currentThread().getName() + " park");
                LockSupport.parkNanos(3_000_000_000L);
                System.out.println("end start thread name: " + Thread.currentThread().getName() + " park");
            }
        };
        Thread thread = new Thread(runnable);
        thread.start();
    }
}

运行结果:

在这里插入图片描述

三秒之后,阻塞三秒之后继续运行。

park(Object blocker)

park(Object blocker):除非有许可,否则出于线程调度目的禁用当前线程
源码:

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

这里的blocker对象是Thread类中的blocker对象,代码如下:

//提供给java.util.concurrent.locks.LockSupport.park的当前调用的参数。
//由(私有)java.util.concurrent.locks.LockSupport.setBlocker设置使用
//java.util.concurrent.locks.LockSupport.getBlocker进行访问
volatile Object parkBlocker;

parkNanos(Object blocker, long nanos)

源码:

public static void parkNanos(Object blocker, long nanos) {
    if (nanos > 0) {
        Thread t = Thread.currentThread();
        setBlocker(t, blocker);
        UNSAFE.park(false, nanos);
        setBlocker(t, null);
    }
}

parkUntil(Object blocker, long deadline)

parkUntil(Object blocker, long deadline):除非指定许可,否则禁用当前线程以进行线程调度,直到指定的期限。
源码:

public static void parkUntil(Object blocker, long deadline) {
    Thread t = Thread.currentThread();
    setBlocker(t, blocker);
    UNSAFE.park(true, deadline);
    setBlocker(t, null);
}

我们使用java API中的例子看看:

public class LockSupportMain6 {
    private final AtomicBoolean locked = new AtomicBoolean(false);
    private final Queue<Thread> waiters
            = new ConcurrentLinkedQueue<Thread>();

    public void lock() {
        boolean wasInterrupted = false;
        Thread current = Thread.currentThread();
        waiters.add(current);

        // 不在队列中时先阻塞或无法获取锁定
        while (waiters.peek() != current ||
                !locked.compareAndSet(false, true)) {
            LockSupport.park(this);
            // 等待时忽略中断 如果park方法是因为被中断而返回,则忽略中断,并且重新设置中断标记,做个标记
            if (Thread.interrupted()) {
                wasInterrupted = true;
            }
        }
        waiters.remove();
        // 退出时重新声明中断状态
        if (wasInterrupted)          
        {
            current.interrupt();
        }
    }
    public void unlock() {
        locked.set(false);
        LockSupport.unpark(waiters.peek());
    }
}

有blocker的可以传递给开发人员更多的现场信息,可以查看到当前线程的阻塞对象,方便定位问题

到此这篇关于教你如何使用Java多线程编程-LockSupport工具类的文章就介绍到这了,更多相关使用java的LockSupport工具类内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • JavaGUI界面实现页面跳转方法

    JavaGUI界面实现页面跳转方法

    这篇文章主要给大家介绍了关于JavaGUI界面实现页面跳转的相关资料, GUI是指图形用户界面,指采用图形方式显示的计算机操作用户界面,需要的朋友可以参考下
    2023-07-07
  • Java设计模式之备忘录模式(Memento模式)介绍

    Java设计模式之备忘录模式(Memento模式)介绍

    这篇文章主要介绍了Java设计模式之备忘录模式(Memento模式)介绍,memento是一个保存另外一个对象内部状态拷贝的对象,这样以后就可以将该对象恢复到原先保存的状态,需要的朋友可以参考下
    2015-03-03
  • 带你了解Java数据结构和算法之哈希表

    带你了解Java数据结构和算法之哈希表

    这篇文章主要为大家介绍了Java数据结构和算法之哈希表,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-01-01
  • Java实现两人五子棋游戏(三) 画出棋子

    Java实现两人五子棋游戏(三) 画出棋子

    这篇文章主要为大家详细介绍了Java实现两人五子棋游戏,画出五子棋的棋子,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-03-03
  • Java人机猜拳实现的思路及方法实例

    Java人机猜拳实现的思路及方法实例

    这篇文章主要给大家介绍了关于Java人机猜拳实现的思路及方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-12-12
  • 在Java中去除字符串末尾的换行符的常用方法小结

    在Java中去除字符串末尾的换行符的常用方法小结

    在日常开发中,字符串操作是非常常见的需求,其中去除字符串末尾的换行符(\n)是一个很有代表性的场景,本文将从 Java 的几种常用方法着手,全面解析如何去除字符串末尾的换行符,并结合代码示例和实际使用场景,帮助大家掌握这一技巧,需要的朋友可以参考下
    2024-12-12
  • 深入了解Java设计模式之策略模式

    深入了解Java设计模式之策略模式

    策略模式属于Java-设计模式中行为模式之一,该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换。本文将通过示例详细讲解这一模式,需要的可以参考一下
    2022-09-09
  • 详解Java中Javassist的使用

    详解Java中Javassist的使用

    常用的一些操作字节码的技术有 ASM、AspectJ、Javassist 等。本文主要为大家介绍了Javassist使用的相关知识,感兴趣的小伙伴可以了解一下
    2023-04-04
  • 详解shiro缓存机制

    详解shiro缓存机制

    Shiro提供了类似于Spring的Cache抽象,即Shiro本身不实现Cache,但是对Cache进行了又抽象,方便更换不同的底层Cache实现。下面通过实例代码给大家分享shiro缓存机制,感兴趣的朋友一起看看吧
    2017-09-09
  • 解决Spring国际化文案占位符失效问题的方法

    解决Spring国际化文案占位符失效问题的方法

    本篇文章主要介绍了解决Spring国际化文案占位符失效问题的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-04-04

最新评论