在上篇博客(【Java并发编程实战】----- AQS(二):获取锁、释放锁)中提到,当一个线程加入到CLH队列中时,如果不是头节点是需要判断该节点是否需要挂起;在释放锁后,需要唤醒该线程的继任节点

lock方法,在调用acquireQueued():

if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;

在acquireQueued()中调用parkAndCheckInterrupt()来挂起当前线程:

private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this);
        return Thread.interrupted();
    }

调用LockSupport.park()方法。对于park():为了线程调度,在许可可用之前禁用当前线程。

释放锁后,需要唤醒该线程继任节点:

public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

在release方法中调用unparkSuccessor()来唤醒该线程的继任节点。在unparkSuccessor()方法中通过LockSupport.unpark()来唤醒。unpark():如果给定线程的许可尚不可用,则使其可用。

LockSupport

LockSupport是用来创建锁和其他同步类的基本线程阻塞原语。每个使用LockSupport的线程都会与一个许可关联,如果该许可可用,并且可在进程中使用,则调用park()将会立即返回,否则可能阻塞。如果许可尚不可用,则可以调用 unpark 使其可用。但是注意许可不可重入,也就是说只能调用一次park()方法,否则会一直阻塞。

LockSupport.park()、LockSupport.unpark()的作用分别是阻塞线程和解除阻塞线程,且park()和unpark()不会遇到“Thread.suspend ()和 Thread.resume所可能引发的死锁”问题。当然park()、unpark()是成对使用。

park():如果许可可用,则使用该许可,并且该调用立即返回;否则,为线程调度禁用当前线程,并在发生以下三种情况之一以前,使其处于休眠状态:

  • 其他某个线程将当前线程作为目标调用 unpark;或者
  • 其他某个线程中断当前线程;或者
  • 该调用不合逻辑地(即毫无理由地)返回。

    其源码实现如下:

    public static void park() {
            unsafe.park(false, 0L);
        }

    unpark:如果给定线程的许可尚不可用,则使其可用。如果线程在 park 上受阻塞,则它将解除其阻塞状态。否则,保证下一次调用 park 不会受阻塞。如果给定线程尚未启动,则无法保证此操作有任何效果。

    其源代码如下:

    public static void unpark(Thread thread) {
            if (thread != null) {
                Object lock = unsafe.getObject(thread, lockOffset);
                synchronized (lock) {
                    if (thread.isAlive()) {
                        unsafe.unpark(thread);
                    }
                }
            }
        }

     

    一般来说park()、unpark()是成对出现的,同时unpark必须要在park执行之后执行,当然并不是说没有不调用unpark线程就会一直阻塞,park有一个方法,它带了时间戳(parkNanos(long nanos):为了线程调度禁用当前线程,最多等待指定的等待时间,除非许可可用。)

     

    参考资料

    1、LockSupport的park和unpark的基本使用,以及对线程中断的响应性

  • 【Java并发编程实战】----- AQS(三):阻塞、唤醒:LockSupport的更多相关文章

    1. JAVA并发编程实战---第三章:对象的共享(2)

      线程封闭 如果仅仅在单线程内访问数据,就不需要同步,这种技术被称为线程封闭,它是实现线程安全性的最简单的方式之一.当某个对象封闭在一个线程中时,这种方法将自动实现线程安全性,即使被封闭的对象本生不是线 ...

    2. JAVA并发编程实战---第三章:对象的共享

      在没有同步的情况下,编译器.处理器以及运行时等都可能对操作的执行顺序进行一些意想不到的调整.在缺乏足够同步的多线程程序中,要对内存操作的执行顺序进行判断几乎无法得到正确的结果. 非原子的64位操作 当 ...

    3. java并发编程实战学习(3)--基础构建模块

      转自:java并发编程实战 5.3阻塞队列和生产者-消费者模式 BlockingQueue阻塞队列提供可阻塞的put和take方法,以及支持定时的offer和poll方法.如果队列已经满了,那么put ...

    4. 【Java并发编程实战】----- AQS(四):CLH同步队列

      在[Java并发编程实战]-–"J.U.C":CLH队列锁提过,AQS里面的CLH队列是CLH同步锁的一种变形.其主要从两方面进行了改造:节点的结构与节点等待机制.在结构上引入了头 ...

    5. 【Java并发编程实战】----- AQS(二):获取锁、释放锁

      上篇博客稍微介绍了一下AQS,下面我们来关注下AQS的所获取和锁释放. AQS锁获取 AQS包含如下几个方法: acquire(int arg):以独占模式获取对象,忽略中断. acquireInte ...

    6. 【Java并发编程实战】—– AQS(四):CLH同步队列

      在[Java并发编程实战]-–"J.U.C":CLH队列锁提过,AQS里面的CLH队列是CLH同步锁的一种变形. 其主要从双方面进行了改造:节点的结构与节点等待机制.在结构上引入了 ...

    7. 那些年读过的书《Java并发编程实战》和《Java并发编程的艺术》三、任务执行框架—Executor框架小结

      <Java并发编程实战>和<Java并发编程的艺术>           Executor框架小结 1.在线程中如何执行任务 (1)任务执行目标: 在正常负载情况下,服务器应用 ...

    8. 【Java并发编程实战】-----&ldquo;J.U.C&rdquo;:Semaphore

      信号量Semaphore是一个控制访问多个共享资源的计数器,它本质上是一个"共享锁". Java并发提供了两种加锁模式:共享锁和独占锁.前面LZ介绍的ReentrantLock就是 ...

    9. 【Java并发编程实战】-----&ldquo;J.U.C&rdquo;:ReentrantReadWriteLock

      ReentrantLock实现了标准的互斥操作,也就是说在某一时刻只有有一个线程持有锁.ReentrantLock采用这种独占的保守锁直接,在一定程度上减低了吞吐量.在这种情况下任何的"读/ ...

    随机推荐

    1. PAT1078 Hashing

      11-散列2 Hashing   (25分) The task of this problem is simple: insert a sequence of distinct positive in ...

    2. javamail实现邮箱验证功能

      javamail是基于SMTP协议和POP3协议的邮件发送与接收系统,在用户注册与登陆的过程中,常需要用到邮箱验证功能,下面是基于javamail的一个简单实现 用户注册后通过servlet得到邮箱地 ...

    3. 【转】设计模式(九)外观模式Facade(结构型)

      设计模式--外观模式Facade(结构型): 1. 概述 外观模式,我们通过外观的包装,使应用程序只能看到外观对象,而不会看到具体的细节对象,这样无疑会降低应用程序的复杂度,并且提高了程序的可维护性. ...

    4. C#颜色和名称样式对照表

      1   Color.AliceBlue 240,248,255 Color.LightSalmon 255,160,122 Color.AntiqueWhite 250,235,215 Color.L ...

    5. Oracle ORA-01555 快照过旧 说明

      oracle高级知识(1) ORA-01555 快照过旧,是数据库中很常见的一个错误,比如当我们的事务需要使用undo来构建CR块的时候,而此时对应的undo 已经不存在了, 这个时候就会报ORA-0 ...

    6. 面向对象程序设计-C++_课时22向上造型

      赋值兼容规则是指在公有派生情况下,一个派生类的对象可以作为基类的对象来使用的情况. 约定类derived是从类base公有派生而来的,则指如下3种情况: (1)派生的对象可以赋给基类的对象.例如: d ...

    7. OPENCV直方图与匹配

      直方图可以用来描述不同的参数和事物,如物体的色彩分布,物体的边缘梯度模版以及目标位置的当前假设的概率分布. 直方图就是对数据进行统计的一种方法,并且将统计值定义到一系列定义好的bin(组距)中,获得一 ...

    8. awk匹配以aaa开头,以bbb结尾的内容,同时aaa和bbb之间还包含ccc

      如果是匹配以A开头,以B结尾的内容,同时A和B之间还包含C的这种怎么做?比如 [root@localhost ~]#cat file aaa grge ddd bbb aaa gege ccc bbb ...

    9. 关于tomcat 配置时一闪而过的问题

      TOMCAT JAVA_HOME or JRE_HOME environment variable is not defined correctly 按照教程已经安装了JDK并设置好了JAVA_HOM ...

    10. 在linux系统中实现各项监控的关键技术(2)--内核态与用户态进程之间的通信netlink

      Netlink 是一种在内核与用户应用间进行双向数据传输的非常好的方式,用户态应用使用标准的 socket API 就可以使用 netlink 提供的强大功能,内核态需要使用专门的内核 API 来使用 ...