51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

LockSupport 工具介绍

简单介绍 {#简单介绍}

LockSupport 是用来创建锁和其他同步类的基本线程阻塞原语,是线程等待唤醒机制的一种实现工具类。

等待唤醒机制 {#等待唤醒机制}

等待唤醒机制 是线程中的一种协作机制。多线程之间不单有竞争锁的情况,还有相互协作的场景。比如线程A执行完某一操作需要挂起一段时间,将运行的机会让给线程B,当线程B执行完任务后就唤醒线程A继续做任务。

Java 提供了 3 种等待唤醒机制:

| 方式 | 等待 | 唤醒 | 执行要求 | |-------------|----------|-----------------|------------------------------------------------| | Object | wait 方法 | notify 方法(随机唤醒) | ①需要获取锁,否则会报错 ②需先调用 wait 才能调用 notify,否则线程会一直等待 | | Condition | await 方法 | signal 方法(随机唤醒) | ①需要获取锁,否则会报错 ②需先调用 await 才能调用 signal,否则线程会一直等待 | | LockSupport | park 方法 | unpark 方法(指定唤醒) | 无 |

其中,LockSupport 类提供的都是静态的方法且对执行没有要求,这让线程可以在任意位置实现等待或唤醒,非常方便。

实现原理 {#实现原理}

LockSupport 类使用 Permit(许可) 概念来做到等待和唤醒线程的功能:

|-----------|-----------------------------------------------------------------------------| | 1 | 每个使用 LockSupport 的线程都有一个 Permit,它相当于通行开关(0:等待 1: 通行),默认值为 0,最大值为 1。 |

当线程调用 park() 方法时:

  • 有许可,即 Permit 值为 1 时,会将 Permit 改成 0,同时正常退出方法。
  • 无许可,即 Permit 值为 0 时,线程会被挂起(线程状态变成 WAIT)。

当线程调用 unpark(thread) 方法时:

  • 指定线程的 Permit 值会变成 1(多次 unpark()Permit 值也是 1),如果指定的线程处在 WAIT 状态,则会被唤醒。

线程调用 park() 方法是否被挂起只与它的许可值相关,因此,如果其他线程先调用 unpark(t1) 方法,t1 线程再调用 park() 是不会被挂起的。

案例演示 {#案例演示}

  • 案例1

|------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | public class LockSupportTest { public static void main(String[] args) throws Exception { Thread t1 = new Thread(() -> { System.out.println(Thread.currentThread().getName() + " 开始等待"); LockSupport.park(); System.out.println(Thread.currentThread().getName() + " 唤醒成功"); }, "t1"); t1.start(); TimeUnit.SECONDS.sleep(1); System.out.println(Thread.currentThread().getName() + " 开始唤醒 t1"); LockSupport.unpark(t1); } } |

运行结果:

|---------------|--------------------------------------| | 1 2 3 | t1 开始等待 main 开始唤醒 t1 t1 唤醒成功 |

从结果可以看出,t1 线程先调用 park() 方法后被挂起等待,1 秒后主线程调用 unpark(t1),最后 t1 被唤醒执行 System.out 输出后结束线程。

  • 案例2

|---------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | public class LockSupportTest { public static void main(String[] args) { Thread t1 = new Thread(() -> { try { TimeUnit.SECONDS.sleep(1); System.out.println(LocalDateTime.now() + " -> " + Thread.currentThread().getName() + " 睡眠了 1 秒"); LockSupport.park(); System.out.println(LocalDateTime.now() + " -> " + Thread.currentThread().getName() + " 结束"); } catch (Exception e) { e.printStackTrace(); } }, "t1"); t1.start(); System.out.println(LocalDateTime.now() + " -> " + Thread.currentThread().getName() + " 提前开始唤醒 t1"); LockSupport.unpark(t1); } } |

运行结果:

|---------------|--------------------------------------------------------------------------------------------------------------------------| | 1 2 3 | 2023-02-21T10:47:11.774 -> main 提前开始唤醒 t1 2023-02-21T10:47:12.755 -> t1 睡眠了 1 秒 2023-02-21T10:47:12.755 -> t1 结束 |

主线程先调用 unpark(t1) 方法,1 秒后 t1 线程再调用 park() 并没有被挂起等待,而是随后立即执行了 System.out 的输出结束线程。

小结 {#小结}

  • LockSupport 通过 Permit 实现等待唤醒机制。
  • LockSupport 通过 park() 挂起线程(Permit 值变成 0),通过 unpark(thread) 唤醒线程(Permit 值变成 1)。
  • 使用 LockSupport 的线程都有一个 Permit 且最大值为 1,即无论调用多少次 unpark(thread),最终 Permit 值都是 1。
赞(0)
未经允许不得转载:工具盒子 » LockSupport 工具介绍