Java并发编程--JUC并发工具类之LockSupport
摘要
-
本文介绍LockSupport相关技术
-
本文基于
jdk1.8
LockSupport介绍
-
LockSupport
是Java并发工具类中的一个重要成员。它提供了一种基于线程的阻塞和唤醒机制,使得线程可以在特定条件下暂停和继续执行。 -
与传统的
wait()
和notify()
方法相比,LockSupport
具有以下优势:- 精准的线程阻塞和唤醒:
LockSupport
提供了精确控制线程阻塞和唤醒的能力。通过调用LockSupport
类的park()
方法,线程可以主动进入阻塞状态,直到其他线程调用了相应线程的unpark()
方法才能被唤醒。相比之下,wait()
和notify()
方法的使用需要依赖于对象的监视器(monitor),并且无法指定特定的线程进行唤醒。 - 不依赖于对象的监视器:
传统的wait()
和notify()
方法需要依赖于对象的监视器(monitor),即在synchronized块中调用。这种依赖关系可能导致代码结构上的限制,而LockSupport
则不依赖于特定的对象,可以在任何位置进行线程的阻塞和唤醒操作。 - 避免死锁问题:
在使用wait()
和notify()
方法时,由于需要依赖于对象的监视器,可能会出现死锁问题,例如线程A等待线程B的通知,而线程B也在等待线程A的通知,导致双方无法继续执行。LockSupport
通过给每个线程关联一个许可(permit
)来避免死锁问题,即使在park()
和unpark()
方法的调用顺序上没有特定的要求。
- 精准的线程阻塞和唤醒:
-
总体而言,
LockSupport
是一种强大而灵活的线程阻塞和唤醒机制,能够满足并发编程中的各种需求。它的设计理念与传统的wait()
和notify()
方法有所不同,提供了更加直观和可控的线程调度方式。然而,在使用LockSupport
时,仍需谨慎处理线程的阻塞和唤醒逻辑,以避免潜在的并发问题。
LockSupport API说明
方法 | 描述 |
---|---|
void park() |
阻塞当前线程,直到被唤醒 |
void park(Object blocker) |
阻塞当前线程,并关联一个阻塞对象 |
void parkNanos(long nanos) |
阻塞当前线程,最多等待指定纳秒时间 |
void parkNanos(Object blocker, long nanos) |
阻塞当前线程,并关联一个阻塞对象,最多等待指定纳秒时间 |
void parkUntil(long deadline) |
阻塞当前线程,直到指定的绝对时间点 |
void parkUntil(Object blocker, long deadline) |
阻塞当前线程,并关联一个阻塞对象,直到指定的绝对时间点 |
void unpark(Thread thread) |
唤醒指定的线程 |
Object getBlocker(Thread thread) |
获取指定线程关联的阻塞对象 |
-
其中,
park()
和unpark()
是最常用的方法,用于线程的阻塞和唤醒操作。 -
其他方法提供了更多灵活的线程阻塞和唤醒方式,如指定阻塞对象、等待一定时间等。
-
getBlocker()
方法可以获取指定线程关联的阻塞对象,以便进行进一步的操作或分析。 -
在
LockSupport
的内部实现中,会使用底层的CAS
操作来控制线程的阻塞和唤醒。当线程调用park()
方法时,会将线程置于等待队列,并将线程的许可状态设置为负数。当调用unpark()
方法时,会将线程的许可状态设置为非负数,从而唤醒等待的线程。
代码示例
示例1–park()
和unpark()
1 | import java.util.concurrent.locks.LockSupport; |
-
在上面的示例中,
waiterThread
线程通过调用LockSupport.park()
进入阻塞状态,直到condition
变为true
。notifierThread
线程在经过一些耗时操作后,将condition
设置为true
,然后调用LockSupport.unpark(waiterThread)
唤醒waiterThread
线程。
示例2–park(Object blocker)
和getBlocker(Thread thread)
1 | import java.util.concurrent.locks.LockSupport; |
-
在上述示例中,我们创建了一个阻塞对象
blocker
。在thread线程中,我们调用LockSupport.park(blocker)
方法阻塞当前线程,并将阻塞对象与线程关联。 -
在主线程中,我们使用
LockSupport.getBlocker(thread)
方法获取与线程关联的阻塞对象,并输出它。
park方法中阻塞对象的作用
-
当调用
LockSupport的park()
方法时,线程将被阻塞,等待被唤醒。park(Object blocker)
方法是park()
方法的一个重载形式,它允许您将一个阻塞对象与当前线程相关联。绑定阻塞对象的作用在于更好地识别和监控线程的阻塞原因。 -
当线程被阻塞时,可以通过调用
getBlocker(Thread thread)
方法来获取与该线程相关联的阻塞对象。这样,您可以在代码中根据阻塞对象进行更精确的条件判断、监控或其他操作。 -
以下是一些使用场景的例子,说明绑定阻塞对象的作用:
- 调试和诊断:当多个线程被阻塞时,可以使用阻塞对象来确定具体是哪个线程被阻塞,以及被阻塞的原因。通过获取阻塞对象,您可以在调试过程中定位问题,并根据阻塞对象的不同采取相应的调试措施。
- 更精确的控制和唤醒:通过关联特定的阻塞对象,您可以在需要唤醒线程时,只选择唤醒与该阻塞对象相关联的线程。这种精确的唤醒机制可以避免不必要的线程唤醒,提高系统的性能和效率。
- 条件等待:在某些情况下,您可能希望线程在特定条件下被阻塞,直到满足某个条件后才被唤醒。通过绑定阻塞对象,您可以根据不同的条件选择不同的阻塞对象,从而实现对不同条件的精确等待和唤醒。
-
绑定阻塞对象的作用是提供更多的上下文信息,帮助您更好地理解和控制线程的阻塞状态。它可以作为一种辅助手段,用于更精确地调试、监控和控制线程的行为。
ReentrantLock的阻塞唤醒机制是基于LockSupport实现的
-
ReentrantLock
是Java中提供的可重入锁的实现,它使用了内部类Sync
来管理锁的状态和线程的阻塞与唤醒。Sync
类内部使用了LockSupport
工具类来实现线程的阻塞和唤醒操作。 -
当一个线程无法获取到
ReentrantLock
的锁时,它会被阻塞,并且进入等待状态,在这种情况下,ReentrantLock
内部会调用LockSupport.park()
方法阻塞当前线程。 -
当其他线程释放了锁或者调用了
ReentrantLock
的unlock()
方法时,被阻塞的线程将会被唤醒,此时ReentrantLock
会通过调用LockSupport.unpark()
方法来唤醒被阻塞的线程。