LockSupport源码分析

目录

  • LockSupport源码分析

    • LockSupport的实现

      • 1. 内部重要的属性:
      • 2. getBlocker(Thread) 与 setBlocker(Thread t, Object arg)源码
      • 3. park的其他几个方法
      • 4. park()/unpark() 与 wait()/notify()区别:

LockSupport源码分析

LockSupport是Java6引入的一个工具类, 用于挂起和唤醒线程;

LockSupport 通过提供park() 和 unpark() 方法实现阻塞线程和解除线程阻塞, 实现阻塞与解除阻塞是基于许可(permit), permit相当于一个信号量,只能取0和1, 默认为0;

  • park(): 若permit为1, 则permit减1为0; 若permit为0, 则阻塞当前线程;
  • unpark(Thread): 若permit为0, permit加1; 若permit为1, permit不变; 但效果都是唤醒指定线程;

由于permit默认为0, 因此一般是先调用park()之后, 当前线程进入阻塞, 然后等待其他线程调用unpark(Thread)唤醒, 或者遇到中断会被唤醒;

LockSupport的实现

LockSupport 是基于 sun.misc.Unsafe 实现的;

1. 内部重要的属性:

  • UNSAFE: Unsafe对象, 提供CAS操作, 获取对象内存中字段的偏移量, 设置对象偏移量对应的字段的值;
  • parkBlockerOffset 存储Thread类对象中parkBlocker字段的偏移量;

    Thread类中, parkBlocker用于存储引起线程阻塞的对象(即: 线程所等待的资源); parkBlocker字段是特地为park()设计的, 在park()调用后, 通过调用setBlocker(Thread t, Object arg)设置线程所等待的资源, 此时用jstack工具进行堆栈分析时, 可以看到该阻塞的线程正在等待的资源arg;

private static final sun.misc.Unsafe UNSAFE;
private static final long parkBlockerOffset;
static {
    try {
        UNSAFE = sun.misc.Unsafe.getUnsafe();
        Class<?> tk = Thread.class;
        parkBlockerOffset = UNSAFE.objectFieldOffset
            (tk.getDeclaredField("parkBlocker"));
        ...
    } catch (Exception ex) { throw new Error(ex); }
}

2. getBlocker(Thread) 与 setBlocker(Thread t, Object arg)源码

getBlocker 与 setBlocker 使用Unsafe类通过内存偏移量设置的原因:

因为要设置的parkBlocker的线程已经被阻塞了, 通过调用Thread对象的set方法由于阻塞无法被执行; 然而使用Unsafe类通过内存偏移地址设置并不受Thread对象被阻塞的影响;

/**
 * 唤醒线程 t
 * @param  t 想要唤醒的线程
 * @return  返回parkBlocker对象
 */
public static Object getBlocker(Thread t) {
    if (t == null)
        throw new NullPointerException();
    return UNSAFE.getObjectVolatile(t, parkBlockerOffset);
}
/**
 * 设置 parkBlocker的值
 * @param t   被阻塞的线程
 * @param arg 引起线程阻塞的资源
 */
private static void setBlocker(Thread t, Object arg) {
    // Even though volatile, hotspot doesn't need a write barrier here.
    UNSAFE.putObject(t, parkBlockerOffset, arg);
}

3. park的其他几个方法

  • park(Object blocker)
  • parkNanos(long nanos)
  • parkNanos(Object blocker, long nanos)
  • parkUntil(long deadline)
  • parkUntil(Object blocker, long deadline)

内部实现源码:

public static void park(Object blocker) {
    Thread t = Thread.currentThread(); // 获取当前线程
    setBlocker(t, blocker); // 设置引起当前线程阻塞的资源
    UNSAFE.park(false, 0L); // 将当前线程阻塞
    setBlocker(t, null); // 该线程被唤醒后, 将blocker置为null
}
// 阻塞当前线程, 最长不超过nanos纳秒
public static void parkNanos(long nanos) {
    if (nanos > 0)
        UNSAFE.park(false, nanos); // 超时自动唤醒
}
public static void parkNanos(Object blocker, long nanos) {
    if (nanos > 0) {
        Thread t = Thread.currentThread();
        setBlocker(t, blocker);
        UNSAFE.park(false, nanos);
        setBlocker(t, null);
    }
}
// 阻塞当前线程, 知道deadline时间(从1970年开始到deadline时间的毫秒数)
public static void parkUntil(long deadline) {
    UNSAFE.park(true, deadline);
}
public static void parkUntil(Object blocker, long deadline) {
    Thread t = Thread.currentThread();
    setBlocker(t, blocker);
    UNSAFE.park(true, deadline);
    setBlocker(t, null);
}

4. park()/unpark() 与 wait()/notify()区别:

  • park(), wait() 都是阻塞当前线程, 但是wait()需要获取synchronize监视器才能调用;
  • park() 可以设置getBlocker, 使用jstack堆栈分析时更加方便;
  • unpark() 可以唤醒指定线程, 而notify()唤醒一个随机的在该监视器上等待的线程, notifyAll()唤醒所有在该监视器上等待的线程;

原文地址:https://www.cnblogs.com/jxkun/p/9375036.html

时间: 2024-11-10 14:15:44

LockSupport源码分析的相关文章

【JUC】JDK1.8源码分析之LockSupport(一)

一.前言 最开始打算分析ReentrantLock,但是分析到最后,发现离不开LockSuport的支持,所以,索性就先开始分析LockSupport,因为它是锁中的基础,是一个提供锁机制的工具类,所以先对其进行分析. 二.LockSupport源码分析 2.1 类的属性 public class LockSupport { // Hotspot implementation via intrinsics API private static final sun.misc.Unsafe UNSA

【JUC】JDK1.8源码分析之ReentrantLock(三)

一.前言 在分析了AbstractQueuedSynchronier源码后,接着分析ReentrantLock源码,其实在AbstractQueuedSynchronizer的分析中,已经提到过ReentrantLock,ReentrantLock表示下面具体分析ReentrantLock源码. 二.ReentrantLock数据结构 ReentrantLock的底层是借助AbstractQueuedSynchronizer实现,所以其数据结构依附于AbstractQueuedSynchroni

【JUC】JDK1.8源码分析之CyclicBarrier(四)

一.前言 有了前面分析的基础,现在,接着分析CyclicBarrier源码,CyclicBarrier类在进行多线程编程时使用很多,比如,你希望创建一组任务,它们并行执行工作,然后在进行下一个步骤之前等待,直至所有的任务都完成,和join很类似,下面,开始分析源码. 二.CyclicBarrier数据结构 分析源码可以知道,CyclicBarrier底层是基于ReentrantLock和AbstractQueuedSynchronizer来实现的,所以,CyclicBarrier的数据结构也依托

Hashtable、ConcurrentHashMap源码分析

Hashtable.ConcurrentHashMap源码分析 为什么把这两个数据结构对比分析呢,相信大家都明白.首先二者都是线程安全的,但是二者保证线程安全的方式却是不同的.废话不多说了,从源码的角度分析一下两者的异同,首先给出二者的继承关系图. Hashtable类属性和方法源码分析 我们还是先给出一张Hashtable类的属性和方法图,其中Entry<K,V>是Hashtable类的静态内部类,该类继承自Map.Entry<K,V>接口.如下将会详细讲解Hashtable类中

【JUC】JDK1.8源码分析之SynchronousQueue(九)

一.前言 本篇是在分析Executors源码时,发现JUC集合框架中的一个重要类没有分析,SynchronousQueue,该类在线程池中的作用是非常明显的,所以很有必要单独拿出来分析一番,这对于之后理解线程池有很有好处,SynchronousQueue是一种阻塞队列,其中每个插入操作必须等待另一个线程的对应移除操作 ,反之亦然.同步队列没有任何内部容量,甚至连一个队列的容量都没有. 二.SynchronousQueue数据结构 由于SynchronousQueue的支持公平策略和非公平策略,所

Java并发系列[2]----AbstractQueuedSynchronizer源码分析之独占模式

在上一篇<Java并发系列[1]----AbstractQueuedSynchronizer源码分析之概要分析>中我们介绍了AbstractQueuedSynchronizer基本的一些概念,主要讲了AQS的排队区是怎样实现的,什么是独占模式和共享模式以及如何理解结点的等待状态.理解并掌握这些内容是后续阅读AQS源码的关键,所以建议读者先看完我的上一篇文章再回过头来看这篇就比较容易理解.在本篇中会介绍在独占模式下结点是怎样进入同步队列排队的,以及离开同步队列之前会进行哪些操作.AQS为在独占模

ReentrantLock 与 AQS 源码分析

ReentrantLock 与 AQS 源码分析 1. 基本结构 ?? 重入锁 ReetrantLock,JDK 1.5新增的类,作用与synchronized关键字相当,但比synchronized更加灵活.ReetrantLock本身也是一种支持重进入的锁,即该锁可以支持一个线程对资源重复加锁,但是加锁多少次,就必须解锁多少次,这样才可以成功释放锁. 1. 继承 没有继承任何类,因为很多操作都使用了组合完成. 2. 实现 Lock, java.io.Serializable ??这里着重介绍

ReentrantReadWriteLock源码分析(一)

此处源码分析,主要是基于读锁,非公平机制,JDK1.8. 问题: 1.ReentrantReadWriteLock是如何创建读锁与写锁? 2.读锁与写锁的区别是什么? 3.锁的重入次数与获取锁的线程数分别是用哪种方式记录的? 4.当队列中出现多个共享模式的线程节点连续排列时,那么当第一个共享模式的线程拿到锁之后,后面的共享线程节点怎么获取锁? 一.创建ReadLock. ReentrantReadWriteLock rrw = new ReentrantReadWriteLock(); publ

[源码分析]ReentrantLock &amp; AbstractQueuedSynchronizer &amp; Condition

首先声明一点: 我在分析源码的时候, 把jdk源码复制出来进行中文的注释, 有时还进行编译调试什么的, 为了避免和jdk原生的类混淆, 我在类前面加了"My". 比如把ReentrantLock改名为了MyReentrantLock, 在源码分析的章节里, 我基本不会对源码进行修改, 所以请忽视这个"My"即可. 一. 简介 锁是什么? 锁是一种标志, 或者是一种资源, 持有锁的线程才可以继续往下执行相应的内容. 未持有锁的线程需要等待这个锁资源. 直到获取到了这个