1.简介

  1. LockSupport 类与每一个使用他的线程都会去关联一个许可证,在默认的情况下调用LockSupport类的方法的线程是不持有许可证的。LockSupport是使用UnSafe类实现的

2.lockSupport的主要方法介绍

2.1 void park()方法

如果调用的线程已经拿到了与LockSupport相关的许可证,则调用LockSupport.park()方法会立即返回,否则会被禁止参与线程的调度,也就是会被阻塞挂起

示例代码

  1. public static void main(String[] args) {
  2. System.out.println("Lock Support begin park");
  3. LockSupport.park();
  4. System.out.println("Lock Support end park");
  5. LockSupport.unpark(Thread.currentThread());
  6. }

执行结果
image.png
注意点:在其他线程调用 unpark(Thread t)将当前线程作为参数时,调用park()方法而被阻塞的线程会返回,另外如果其他线线程调用了阻塞线程的 interrupt()方法时,设置了中断标记或者线程被虚假唤醒,则阻塞线程也会返回,所以在调用park方法时最好使用循环条件

示例代码**

  1. static ExecutorService threadPoolExecutor = new ThreadPoolExecutor(1,
  2. 1,
  3. 0L,
  4. TimeUnit.MILLISECONDS,
  5. new LinkedBlockingQueue<>(5),
  6. Executors.defaultThreadFactory(),
  7. new ThreadPoolExecutor.AbortPolicy());
  8. public static void main(String[] args) {
  9. Thread main = Thread.currentThread();
  10. threadPoolExecutor.submit(() -> {
  11. try {
  12. Thread.sleep(2000);
  13. //LockSupport.unpark(main);
  14. main.interrupt();
  15. } catch (InterruptedException e) {
  16. e.printStackTrace();
  17. }
  18. });
  19. System.out.println("Lock Support begin park");
  20. LockSupport.park();
  21. System.out.println("Lock Support end park");
  22. threadPoolExecutor.shutdown();
  23. }

可以看到 LockSupport.unpark()方法和 interrupt()方法都会中断park()方法,且不会抛出任何异常

2.2 void unpark(Thread t)方法

当一个线程调用 unpark() 方法的时候,如果参数 thread 线程没有持有 thread 和 LockSupport类关联的许可证,则让 thread线程持有。如果thread线程之前因为调用park()方法而被挂起,则调用 unpark() 方法后,该线程会被唤醒,如果thread 之前没有调用 park() 方法,则调用 unpark() 后再次调用 park()会立即返回

示例代码
**

  1. public static void main(String[] args) throws InterruptedException {
  2. Thread t1 = new Thread(() -> {
  3. System.out.println("child Thread begin park");
  4. while (!Thread.currentThread().isInterrupted()) {
  5. LockSupport.park();
  6. }
  7. System.out.println("child Thread end park");
  8. }, "a");
  9. Utils.getThreadPool().submit(t1);
  10. System.out.println("main call child Thread");
  11. Thread.sleep(1000);
  12. //LockSupport.unpark(t1); //子线程并不能被唤醒,因为外面嵌套循环条件
  13. //这里关闭线程就是中断了线程池中正在运行的线程,会输出 “child Thread end park”
  14. Utils.getThreadPool().shutdownNow();
  15. }

执行结果:
image.png

2.3 void parkNanos(long nanos) 方法

和 park() 方法相似,拿到许可证后会立即返回,拿不到许可证则会等待,挂起 nanos 时间后会修改为自动返回

2.4 void park(Object blocker) 方法

park()方法还可以支持带有参数的方法,当线程内部没有许可证的情况下而被挂起,则这个blocker对象会被记录到该线程的内部

作用:
我们可以使用诊断工具来观察线程阻塞的原因,诊断工具是通过调用 getBlocker()方法获取blocker对象的
所以JDK推荐我们使用带有blocker的参数的park()方法,并且blocker设置为this,就可以知道那个线程被阻塞了
**

示例代码:

  1. public class LockSupportTest03 {
  2. public void testPark(){
  3. LockSupport.park(this);
  4. }
  5. public static void main(String[] args){
  6. LockSupportTest03 test = new LockSupportTest03();
  7. test.testPark();
  8. }
  9. }

我们可以使用 jps 和 jstack 两个命令查询对应的信息
如下:

  1. F:\code\test>jps
  2. 20228 Jps
  3. 20292 Launcher
  4. 20376 LockSupportTest03
  5. main" #1 prio=5 os_prio=0 tid=0x0000021f87a8a800 nid=0x2ea8 waiting on condition [0x000000b193aff000]
  6. java.lang.Thread.State: WAITING (parking)
  7. at sun.misc.Unsafe.park(Native Method)
  8. - parking to wait for <0x00000007822c6d38> (a cn.jn.lhm.part01.LockSupportTest03)
  9. at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
  10. at cn.jn.lhm.part01.LockSupportTest03.testPark(LockSupportTest03.java:12)
  11. at cn.jn.lhm.part01.LockSupportTest03.main(LockSupportTest03.java:17)

可能出现的问题:
jps命令不可用原因

源码解读:

  1. public static void park(Object blocker) {
  2. //获取当前的线程
  3. Thread t = Thread.currentThread();
  4. //设置该线程的blocker变量
  5. setBlocker(t, blocker);
  6. //挂起线程
  7. UNSAFE.park(false, 0L);
  8. //线程激活后清楚blocker
  9. setBlocker(t, null);
  10. }

2.5 void parkUtil(Object blocker, long deadline) 方法

源码:

  1. public static void parkUntil(Object blocker, long deadline) {
  2. Thread t = Thread.currentThread();
  3. setBlocker(t, blocker);
  4. //这里的 deadline 是指的是从1970开始计算的时间数 ms
  5. UNSAFE.park(true, deadline);
  6. setBlocker(t, null);
  7. }

3.实战LockSupport

设计一个线程先进先出的锁

示例代码
**

  1. public class FIFOMutex {
  2. private AtomicBoolean locked = new AtomicBoolean(false);
  3. private Queue<Thread> queue = new ConcurrentLinkedQueue<>();
  4. public void lock(){
  5. boolean isInterrupted = false;
  6. Thread thread = Thread.currentThread();
  7. queue.add(thread);
  8. while (queue.peek() != thread || !locked.compareAndSet(false, true)){
  9. LockSupport.park(this);
  10. if (thread.isInterrupted()){
  11. isInterrupted = true;
  12. }
  13. }
  14. queue.remove(thread);
  15. if (isInterrupted){
  16. thread.interrupt();
  17. }
  18. }
  19. public void unlock(){
  20. //cas 恢复成没有获取锁的样子
  21. locked.set(false);
  22. //释放队列头部的线程获取的锁
  23. LockSupport.unpark(queue.peek());
  24. }
  25. }