详解Java多线程编程中LockSupport类的线程阻塞用法

 更新时间:2016年07月12日 08:58:56   作者:skywangkw  
LockSupport类提供了park()和unpark()两个方法来实现线程的阻塞和唤醒,下面我们就来详解Java多线程编程中LockSupport类的线程阻塞用法:

LockSupport是用来创建锁和其他同步类的基本线程阻塞原语。
LockSupport中的park() 和 unpark() 的作用分别是阻塞线程和解除阻塞线程,而且park()和unpark()不会遇到“Thread.suspend 和 Thread.resume所可能引发的死锁”问题。
因为park() 和 unpark()有许可的存在;调用 park() 的线程和另一个试图将其 unpark() 的线程之间的竞争将保持活性。

基本用法
LockSupport 很类似于二元信号量(只有1个许可证可供使用),如果这个许可还没有被占用,当前线程获取许可并继 续 执行;如果许可已经被占用,当前线 程阻塞,等待获取许可。

public static void main(String[] args)
{
   LockSupport.park();
   System.out.println("block.");
}

运行该代码,可以发现主线程一直处于阻塞状态。因为 许可默认是被占用的 ,调用park()时获取不到许可,所以进入阻塞状态。

如下代码:先释放许可,再获取许可,主线程能够正常终止。LockSupport许可的获取和释放,一般来说是对应的,如果多次unpark,只有一次park也不会出现什么问题,结果是许可处于可用状态。

public static void main(String[] args)
{
   Thread thread = Thread.currentThread();
   LockSupport.unpark(thread);//释放许可
   LockSupport.park();// 获取许可
   System.out.println("b");
}

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

public static void main(String[] args) throws Exception
{
 Thread thread = Thread.currentThread();
 
 LockSupport.unpark(thread);
 
 System.out.println("a");
 LockSupport.park();
 System.out.println("b");
 LockSupport.park();
 System.out.println("c");
}

这段代码打印出a和b,不会打印c,因为第二次调用park的时候,线程无法获取许可出现死锁。

下面我们来看下LockSupport对应中断的响应性

public static void t2() throws Exception
{
 Thread t = new Thread(new Runnable()
 {
  private int count = 0;

  @Override
  public void run()
  {
   long start = System.currentTimeMillis();
   long end = 0;

   while ((end - start) <= 1000)
   {
    count++;
    end = System.currentTimeMillis();
   }

   System.out.println("after 1 second.count=" + count);

  //等待或许许可
   LockSupport.park();
   System.out.println("thread over." + Thread.currentThread().isInterrupted());

  }
 });

 t.start();

 Thread.sleep(2000);

 // 中断线程
 t.interrupt();

 
 System.out.println("main over");
}

最终线程会打印出thread over.true。这说明 线程如果因为调用park而阻塞的话,能够响应中断请求(中断状态被设置成true),但是不会抛出InterruptedException 。

LockSupport函数列表

// 返回提供给最近一次尚未解除阻塞的 park 方法调用的 blocker 对象,如果该调用不受阻塞,则返回 null。
static Object getBlocker(Thread t)
// 为了线程调度,禁用当前线程,除非许可可用。
static void park()
// 为了线程调度,在许可可用之前禁用当前线程。
static void park(Object blocker)
// 为了线程调度禁用当前线程,最多等待指定的等待时间,除非许可可用。
static void parkNanos(long nanos)
// 为了线程调度,在许可可用前禁用当前线程,并最多等待指定的等待时间。
static void parkNanos(Object blocker, long nanos)
// 为了线程调度,在指定的时限前禁用当前线程,除非许可可用。
static void parkUntil(long deadline)
// 为了线程调度,在指定的时限前禁用当前线程,除非许可可用。
static void parkUntil(Object blocker, long deadline)
// 如果给定线程的许可尚不可用,则使其可用。
static void unpark(Thread thread)


LockSupport示例
对比下面的“示例1”和“示例2”可以更清晰的了解LockSupport的用法。
示例1

public class WaitTest1 {

  public static void main(String[] args) {

    ThreadA ta = new ThreadA("ta");

    synchronized(ta) { // 通过synchronized(ta)获取“对象ta的同步锁”
      try {
        System.out.println(Thread.currentThread().getName()+" start ta");
        ta.start();

        System.out.println(Thread.currentThread().getName()+" block");
        // 主线程等待
        ta.wait();

        System.out.println(Thread.currentThread().getName()+" continue");
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }

  static class ThreadA extends Thread{

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

    public void run() {
      synchronized (this) { // 通过synchronized(this)获取“当前对象的同步锁”
        System.out.println(Thread.currentThread().getName()+" wakup others");
        notify();  // 唤醒“当前对象上的等待线程”
      }
    }
  }
}

示例2

import java.util.concurrent.locks.LockSupport;

public class LockSupportTest1 {

  private static Thread mainThread;

  public static void main(String[] args) {

    ThreadA ta = new ThreadA("ta");
    // 获取主线程
    mainThread = Thread.currentThread();

    System.out.println(Thread.currentThread().getName()+" start ta");
    ta.start();

    System.out.println(Thread.currentThread().getName()+" block");
    // 主线程阻塞
    LockSupport.park(mainThread);

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

  static class ThreadA extends Thread{

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

    public void run() {
      System.out.println(Thread.currentThread().getName()+" wakup others");
      // 唤醒“主线程”
      LockSupport.unpark(mainThread);
    }
  }
}

运行结果:

main start ta
main block
ta wakup others
main continue

说明:park和wait的区别。wait让线程阻塞前,必须通过synchronized获取同步锁。

相关文章

  • 深入理解 CAS 算法原理已经在jdk中的运用

    深入理解 CAS 算法原理已经在jdk中的运用

    这篇文章主要介绍了深入理解 CAS 算法原理已经在jdk中的运用,帮助大家更好的使用Java,感兴趣的朋友可以了解下
    2020-12-12
  • Spring Mvc中传递参数方法之url/requestMapping详解

    Spring Mvc中传递参数方法之url/requestMapping详解

    在开发中,参数传递是必不可少的一个功能,下面这篇文章主要给大家介绍了关于Spring Mvc中传递参数方法之url/requestMapping的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考借鉴,下面来一起看看吧。
    2017-07-07
  • Java split()方法中的特殊符号举例详解

    Java split()方法中的特殊符号举例详解

    Java中的split方法可以将一个字符串按照指定的分隔符进行分割,返回一个字符串数组,这篇文章主要给大家介绍了关于Java split()方法中的特殊符号的相关资料,需要的朋友可以参考下
    2023-07-07
  • spring IOC容器的Bean管理XML自动装配过程

    spring IOC容器的Bean管理XML自动装配过程

    这篇文章主要为大家介绍了spring IOC容器Bean管理基于XML的自动装配过程,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-05-05
  • Java中String类使用方法总结

    Java中String类使用方法总结

    这篇文章主要介绍了Java中String类的使用方法,文章简单易懂,结尾有实例代码帮助大家理解学习,感兴趣的朋友可以了解下
    2020-06-06
  • mybatis 使用jdbc.properties文件设置不起作用的解决方法

    mybatis 使用jdbc.properties文件设置不起作用的解决方法

    这篇文章主要介绍了mybatis 使用jdbc.properties文件设置不起作用的解决方法,需要的朋友可以参考下
    2018-03-03
  • rabbitmq使用springboot实现direct模式(最新推荐)

    rabbitmq使用springboot实现direct模式(最新推荐)

    这篇文章主要介绍了rabbitmq使用springboot实现direct模式,本文通过示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-07-07
  • Java使用JDBC连接数据库

    Java使用JDBC连接数据库

    本文详细讲解了Java使用JDBC连接数据库,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-12-12
  • IntelliJ IDEA 2020安装使用教程详解

    IntelliJ IDEA 2020安装使用教程详解

    这篇文章主要介绍了IntelliJ IDEA 2020安装使用教程,本文通过图文实例相结合给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-07-07
  • Java @Scheduled定时器用法解析

    Java @Scheduled定时器用法解析

    这篇文章主要介绍了Java @Scheduled定时器用法解析,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-05-05

最新评论