LockSupport——一个让JVM崩溃的并发工具类

LockSupport

Unsafe,提供给Java直接操作内存的方法,是极不安全的,不受JVM管理,容易出现内存泄露。Unsafe使用了单例模式,通过静态getUnsafe()方法获取Unsafe对象。

LockSupport 底层是基于Usafe,实现线程的挂起和唤醒的工具类。内部方法都是静态的,可以在任何地方任何时候进行调用。LockSupport一般也不太使用,也是因为它底层也是直接对Unsafe对象的封装,使用起来还是比较危险的。

其主要方法:

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

LockSupport类与每个使用它的线程都会关联一个许可证,默认情况下调用LockSupport类的方法等线程是不持有许可证等。当一个线程调用了park()方法,会将自己阻塞挂起,不再参与线程的调度。在其他线程调用unpark(Thread thread) 方法并且将调用park方法的线程作为参数时,调用park方法而被阻塞的线程会被唤醒返回。

如果一个线程调用unpark方法时,参数thread线程没有持有thread与LockSupport类关联的许可,则让thread线程持有。如果thread之前因调用park()而被挂起,则调用unpark后,参数thread线程会被唤醒。如果thread之前没有调用park(),则调用unpark()之后,参数线程thread再调用park方法,会立刻返回。也就是说park()与unpark()调用不分先后。

注意:park()与unpark()应成对出现,即一个线程执行park()之后,必须有一个线程执行unpark()操作(也可以自己唤醒自己,但是需要unpark()先执行,再执行park()操作,但是这样并没有什么意义),否则执行park操作的线程将会永久的被挂起(也可以通过使用parkNanos(long nanos)在挂起数秒后自动唤醒)。虽然unpark操作可以在park()操作之前执行,看似很灵活,但要注意一个unpark操作只会唤醒一次park()操作,且执行unpark()操作的线程,必须在执行park()操作的线程返回之后才能结束,否则执行park操作的线程将跟着一起结束。

一次unpark()操作只会唤醒一次park()操作:

@Test
public void LockSupportTest() throws InterruptedException{
  ParkThread thread = new ParkThread();
  thread.start();
  LockSupport.unpark(thread);
  System.out.println("主线程休眠10s……");
  Thread.sleep(10 * 1000);
  System.out.println("主线程允许结束……");
}

class ParkThread extends Thread{
  @Override
  public void run() {
    System.out.println("ParkThread is begin……");
    LockSupport.park();
    System.out.println("子线程第一次被唤醒……");
    try{
      // 线程休眠 2S,等到主线程先执行完 unpark()方法
      Thread.sleep(2 * 1000);
      LockSupport.park();
      System.out.println("子线程第二次被唤醒……");
    }catch (InterruptedException e){
      System.out.println(e);
    }
    System.out.println("子线程运行结束……");
  }
}
//运行结果///
ParkThread is begin……
主线程休眠10s……
子线程第一次被唤醒……
主线程允许结束……

子线程在第一次唤醒之后,第二次执行park()操作仍然被挂起,并没有得到执行,未正常执行结束。

unpark()操作的线程必须在park()操作的线程之后结束:

@Test
public void LockSupportTest() throws InterruptedException{
  ParkThread thread = new ParkThread();
  thread.start();
  LockSupport.unpark(thread);
  System.out.println("主线程休眠1s……");
  Thread.sleep(2 * 1000);
  //thread.join();
  System.out.println("主线程允许结束……");
}

class ParkThread extends Thread{
  @Override
  public void run() {
    try{
      System.out.println("ParkThread is begin……");
      LockSupport.park();
      System.out.println("子线程被唤醒……");
      Thread.sleep(5 * 1000);
    }catch (InterruptedException e){
      System.out.println(e);
    }
    System.out.println("子线程运行结束……");
  }
}
//运行结果///
ParkThread is begin……
主线程休眠1s……
子线程被唤醒……
主线程运行结束……
thread.join();运行结果///
ParkThread is begin……
主线程休眠1s……
子线程被唤醒……
子线程运行结束……
主线程允许结束……  

当主线程运行结束之后,程序也就退出,但是子线程并没有正常执行结束,这种情况可以配合join()一起使用。

在挂起线程时,建议使用带有blocker参数的方法:park(Object blocker)或parkNanos(Object blocker, long nanos),当现在没有持有许可被挂起的情况下,blocker对象会被记录在该线程内部,使用stack pid命令查看堆栈时可以看到被挂起线程的信息。

关注公众号,干活第一时间收到!
在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值