【打怪升级】【juc】关于LockSupport

发布时间 2023-04-02 23:27:18作者: 青柠_fisher
通过juc下LockSupport,可以达到阻塞和唤醒线程的操作

 

  • LockSupport

  LockSupport是juc下一个线程阻塞唤醒的工具类。它的主要方法有:

    

  每个使用LockSupport的线程,会有一个许可;调用park会立即返回,否则会被阻塞。

  如果许可不可用,则可以调用unpark供其可使用,但是许可不可重入,例如在park()后接着park(),这样做可能会导致线程永远无法唤醒。

  对于park和unpark来说,它们本身就是一个阻塞和唤醒的实现,但是值得注意的是,一个线程的最大许可只会有一个。

  

  unpark:对于已经开始的线程,设置一个许可,对于未开始的线程可能不会有效果。

  park:如果当前线程许可>0 ,那么会继续执行,否则就会阻塞等待。什么时候会被唤醒?调用unpark对当前线程,或interrupt当前线程。

  所以,park在业务中 我们尽量保证将线程放在循环中,并前置校验当前的状态。

 

  • wait/notify

public final native void wait(long timeout) throws InterruptedException;
 
public final void wait() throws InterruptedException
 
public final void wait(long timeout, int nanos) throws InterruptedException

  首先说说wait,wait提供了几种重载方法,最终都会执行wait(time)的方法,它是属于Object包下的,调用它会导致当前线程阻塞,直到其他线程调用notify、notifyAll或等待执行时间自动唤醒,并且wait必须在同步块中使用,而且会释放锁。

  notify,用于唤醒wait的线程,如果没有wait的线程则会忽略,并且唤醒的线程会重新获取锁。notifyAll会唤醒所有持有相同的锁的阻塞线程同时唤醒进行竞争消费。

  但是这里又有wait和sleep的区别:

  wait sleep都可以让线程阻塞,并且都可被中断。

  sleep是Thread下的方法,而wait是Object的 而且wait比如在同步代码块中执行。

  sleep时,并不会释放锁,而wait会释放锁。

  sleep在指定时间后会主动退出,而wait如果不设置时间自动唤醒会一直阻塞。

  

 

  • interrupt

  如何在一个线程中暂停另一个线程的运行?

  stop方法?stop已经被禁用,因为stop是强行终止挂起线程,但是这种方式是很危险的,比如线程还没来得及释放锁,那其他线程将永远获取不到资源。

  所以我们要使用interrupt, 它不会像stop一样中断一个正在运行的线程,而线程会执行下去,但会设置线程中的中断状态,线程会一直检测这个标记量以判断线程是否需要被立即终止。

  但是,如果在一个阻塞线程中使用interrupt, 会产生一个InterruptedException异常,结束线程的运行。如果我们自定义捕获异常后,可以做一些额外工作,如终止、继续等

  所以,interrupt一般用在线程中的循环中,控制线程中断的状态。

 

  • LockSupport和wait

  经过上文介绍,我们对wait、notify和LockSupport有了基本的认识。那我们推荐使用哪种方式呢?

  举个例子,例如线程B要通知线程A唤醒,那么前置条件是什么?是要确保线程A必须开始并且持有相同的锁并且已经被阻塞了。那么如果线程A这时候没有达到这个条件,而后续达到了这个条件,那么线程B在notify时无法唤醒线程A,而线程A可能也面临永远无法唤醒的情况。那这种情况如果用LockSupport去处理会有什么好处呢?如果我们用park、unpark,那么在线程启动后,如果unpark在前,那么会在线程中添加一个许可,最大也只能有一个许可,这时如果达到了park条件,发现已经有许可就会继续执行;而如果线程先被park,那就会阻塞,直到被unpark唤醒。

  但是这里需要注意一个问题,如果我们多次park会怎么样?如果我们又unpark了多次会怎么样?

  其实对于LockSupport来说,park和unpark其实就是重置了内部的一个count,调用park会将count重置为0,那么如果重置之前是1,则直接退出,否则就被阻塞;调用unpark时,会将count重置为1,那么如果重置之前是0就将线程唤醒,否则直接退出。

  所以,如果我们多次park和unpark后,会导致线程无法被唤醒。

  推荐先park、后unpark。