摘要:初始时,为,当调用方法时,线程的加,当调用方法时,如果为,则调用线程进入阻塞状态。该对象一般供监视诊断工具确定线程受阻塞的原因时使用。
本文首发于一世流云的专栏:https://segmentfault.com/blog...一、LockSupport类简介
LockSupport类,是JUC包中的一个工具类,是用来创建锁和其他同步类的基本线程阻塞原语。(Basic thread blocking primitives for creating locks and other synchronization classes)
LockSupport类的核心方法其实就两个:park()和unark(),其中park()方法用来阻塞当前调用线程,unpark()方法用于唤醒指定线程。
这其实和Object类的wait()和signial()方法有些类似,但是LockSupport的这两种方法从语意上讲比Object类的方法更清晰,而且可以针对指定线程进行阻塞和唤醒。
LockSupport类使用了一种名为Permit(许可)的概念来做到阻塞和唤醒线程的功能,可以把许可看成是一种(0,1)信号量(Semaphore),但与 Semaphore 不同的是,许可的累加上限是1。1.1 使用示例
初始时,permit为0,当调用unpark()方法时,线程的permit加1,当调用park()方法时,如果permit为0,则调用线程进入阻塞状态。
来看一个例子:
假设现在需要实现一种FIFO类型的独占锁,可以把这种锁看成是ReentrantLock的公平锁简单版本,且是不可重入的,就是说当一个线程获得锁后,其它等待线程以FIFO的调度方式等待获取锁。
public class FIFOMutex { private final AtomicBoolean locked = new AtomicBoolean(false); private final Queuewaiters = new ConcurrentLinkedQueue (); public void lock() { Thread current = Thread.currentThread(); waiters.add(current); // 如果当前线程不在队首,或锁已被占用,则当前线程阻塞 // NOTE:这个判断的意图其实就是:锁必须由队首元素拿到 while (waiters.peek() != current || !locked.compareAndSet(false, true)) { LockSupport.park(this); } waiters.remove(); // 删除队首元素 } public void unlock() { locked.set(false); LockSupport.unpark(waiters.peek()); } }
测试用例:
public class Main { public static void main(String[] args) throws InterruptedException { FIFOMutex mutex = new FIFOMutex(); MyThread a1 = new MyThread("a1", mutex); MyThread a2 = new MyThread("a2", mutex); MyThread a3 = new MyThread("a3", mutex); a1.start(); a2.start(); a3.start(); a1.join(); a2.join(); a3.join(); assert MyThread.count == 300; System.out.print("Finished"); } } class MyThread extends Thread { private String name; private FIFOMutex mutex; public static int count; public MyThread(String name, FIFOMutex mutex) { this.name = name; this.mutex = mutex; } @Override public void run() { for (int i = 0; i < 100; i++) { mutex.lock(); count++; System.out.println("name:" + name + " count:" + count); mutex.unlock(); } } }
上述FIFOMutex 类的实现中,当判断锁已被占用时,会调用LockSupport.park(this)方法,将当前调用线程阻塞;当使用完锁时,会调用LockSupport.unpark(waiters.peek())方法将等待队列中的队首线程唤醒。
通过LockSupport的这两个方法,可以很方便的阻塞和唤醒线程。但是LockSupport的使用过程中还需要注意以下几点:
park方法的调用一般要方法一个循环判断体里面。
如上述示例中的:
while (waiters.peek() != current || !locked.compareAndSet(false, true)) { LockSupport.park(this); }
之所以这样做,是为了防止线程被唤醒后,不进行判断而意外继续向下执行,这其实是一种Guarded Suspension的多线程设计模式。
park方法是会响应中断的,但是不会抛出异常。(也就是说如果当前调用线程被中断,则会立即返回但不会抛出中断异常)
park的重载方法park(Object blocker),会传入一个blocker对象,所谓Blocker对象,其实就是当前线程调用时所在调用对象(如上述示例中的FIFOMutex对象)。该对象一般供监视、诊断工具确定线程受阻塞的原因时使用。
二、LockSupport类/方法声明类声明:
方法声明:
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/71523.html
摘要:整个包,按照功能可以大致划分如下锁框架原子类框架同步器框架集合框架执行器框架本系列将按上述顺序分析,分析所基于的源码为。后,根据一系列常见的多线程设计模式,设计了并发包,其中包下提供了一系列基础的锁工具,用以对等进行补充增强。 showImg(https://segmentfault.com/img/remote/1460000016012623); 本文首发于一世流云专栏:https...
摘要:开始获取锁终于轮到出场了,的调用过程和完全一样,同样拿不到锁,然后加入到等待队列队尾然后,在阻塞前需要把前驱结点的状态置为,以确保将来可以被唤醒至此,的执行也暂告一段落了安心得在等待队列中睡觉。 showImg(https://segmentfault.com/img/remote/1460000016012467); 本文首发于一世流云的专栏:https://segmentfault...
摘要:的引入先来看下,为什么有了,还要引入使得多个读线程同时持有读锁只要写锁未被占用,而写锁是独占的。部分常量的比特位表示如下另外,相比,对多核进行了优化,可以看到,当核数超过时,会有一些自旋操作示例分析假设现在有三个线程。 showImg(https://segmentfault.com/img/remote/1460000016012263); 本文首发于一世流云的专栏:https://...
摘要:在时,引入了包,该包中的大多数同步器都是基于来构建的。框架提供了一套通用的机制来管理同步状态阻塞唤醒线程管理等待队列。指针用于在结点线程被取消时,让当前结点的前驱直接指向当前结点的后驱完成出队动作。 showImg(https://segmentfault.com/img/remote/1460000016012438); 本文首发于一世流云的专栏:https://segmentfau...
摘要:公平策略在多个线程争用锁的情况下,公平策略倾向于将访问权授予等待时间最长的线程。使用方式的典型调用方式如下二类原理的源码非常简单,它通过内部类实现了框架,接口的实现仅仅是对的的简单封装,参见原理多线程进阶七锁框架独占功能剖析 showImg(https://segmentfault.com/img/remote/1460000016012582); 本文首发于一世流云的专栏:https...
阅读 2746·2023-04-26 01:47
阅读 3574·2023-04-25 23:45
阅读 2371·2021-10-13 09:39
阅读 579·2021-10-09 09:44
阅读 1757·2021-09-22 15:59
阅读 2670·2021-09-13 10:33
阅读 1628·2021-09-03 10:30
阅读 619·2019-08-30 15:53