JAVA并发--LockSupport

LockSupport概览

Basic thread blocking primitives for creating locks and other synchronization classes.用来创建锁及其他同步类的基础线程阻塞原语。这是java doc中的解释,以下是一个先进先出 (first-in-first-out) 非重入锁类的框架。

 * class FIFOMutex {
 *   private final AtomicBoolean locked = new AtomicBoolean(false); //原子
 *   private final Queue<Thread> waiters  //并发库中的链式队列保存线程
 *     = new ConcurrentLinkedQueue<Thread>();
 *
 *   public void lock() {  //
 *     boolean wasInterrupted = false;
 *     Thread current = Thread.currentThread();
 *     waiters.add(current);
 *
 *     // Block while not first in queue or cannot acquire lock
 *     while (waiters.peek() != current ||
 *            !locked.compareAndSet(false, true)) {//无法获取锁,则阻塞当前线程
 *        LockSupport.park(this);
 *        if (Thread.interrupted()) // ignore interrupts while waiting
 *          wasInterrupted = true;
 *     }
 *
 *     waiters.remove();
 *     if (wasInterrupted)          // reassert interrupt status on exit
 *        current.interrupt();
 *   }
 *
 *   public void unlock() {
 *     locked.set(false);
 *     LockSupport.unpark(waiters.peek());
 *   }  

  上述代码简单实现的锁功能,主要使用了LockSupport的park和unpark阻塞和释放线程,AtomicBoolean的CAS操作来判断是否持有锁ConcurrentLinkedQueue来保存等待线程,此队列是一个线程安全的队列,当前仅当线程在等待队列队首且持有锁才会跳出while循环,从等待队列中移除。

locked.compareAndSet(false, true)的语意为,期望AtomicBoolean对象的值为false,并设置值为true,可以做为是否持有锁的判断

LockSupport源码

方法摘要
static Object getBlocker(Thread t) 
          返回提供给最近一次尚未解除阻塞的 park 方法调用的 blocker 对象,如果该调用不受阻塞,则返回 null。
static void park() 
          为了线程调度,禁用当前线程,除非许可可用。
static void park(Object blocker) 
          为了线程调度,在许可可用之前禁用当前线程。
static void parkNanos(long nanos) 
          为了线程调度禁用当前线程,最多等待指定的等待时间,除非许可可用。
static void parkNanos(Object blocker, long nanos) 
          为了线程调度,在许可可用前禁用当前线程,并最多等待指定的等待时间。
static void parkUntil(long deadline) 
          为了线程调度,在指定的时限前禁用当前线程,除非许可可用。
static void parkUntil(Object blocker, long deadline) 
          为了线程调度,在指定的时限前禁用当前线程,除非许可可用。
static void unpark(Thread thread) 
          如果给定线程的许可尚不可用,则使其可用。

LockSupport提供的全部为static修饰的静态方法,作为一个工具类在使用。主要提供了阻塞线程,和解除阻塞线程的方法。

    // Hotspot implementation via intrinsics API
    private static final Unsafe unsafe = Unsafe.getUnsafe(); //持有一个Unsafe的实例
    private static final long parkBlockerOffset;  //偏移量

    static {
        try {
            parkBlockerOffset = unsafe.objectFieldOffset   //初始化获取属性偏移量
                (java.lang.Thread.class.getDeclaredField("parkBlocker"));
        } catch (Exception ex) { throw new Error(ex); }
    }

  

上一章中我们探讨过unsafe 的CAS用法,这里同样,LockSupport采用Unsafe类来获取属性修改属性,而且parkBlocker是Thread类的成员变量

    /**
     * The argument supplied to the current call to
     * java.util.concurrent.locks.LockSupport.park.
     * Set by (private) java.util.concurrent.locks.LockSupport.setBlocker
     * Accessed using java.util.concurrent.locks.LockSupport.getBlocker
     */
    volatile Object parkBlocker;

  同样是volatile修饰,意为阻塞者,阻塞当前线程的 对象,这个属性为我们提供了可以监控线程被阻塞的信息方法。

 private static void setBlocker(Thread t, Object arg) {
        // Even though volatile, hotspot doesn‘t need a write barrier here.
        unsafe.putObject(t, parkBlockerOffset, arg);
    }

/**
     * Returns the blocker object supplied to the most recent
     * invocation of a park method that has not yet unblocked, or null
     * if not blocked.  The value returned is just a momentary
     * snapshot -- the thread may have since unblocked or blocked on a
     * different blocker object.
     *
     * @param t the thread
     * @return the blocker
     * @throws NullPointerException if argument is null
     * @since 1.6
     */
    public static Object getBlocker(Thread t) {
        if (t == null)
            throw new NullPointerException();
        return unsafe.getObjectVolatile(t, parkBlockerOffset);//根据属性偏移量获取属性值
    }

  为什么通过unsafe来获取线程的parkBlocker变量,而不是通过set和get方法获取?这是因为parkBlocker只有在线程被阻塞时才有意义,而此时set和get方法是无法通过线程对象调用的。

    public native Object getObjectVolatile(Object var1, long var2);

    public native void putObjectVolatile(Object var1, long var2, Object var4);//var2 为属性在var1中的偏移量,var4为要设置的属性值

  这是Unsafe类提供的native方法,具体实现在这里不再深究。

上面探索了LockSupport中对阻塞者信息的处理,下面来看下阻塞及解除阻塞函数。

    public static void park(Object blocker) {
        Thread t = Thread.currentThread(); //当前线程
        setBlocker(t, blocker); //设置被谁阻塞
        unsafe.park(false, 0L);  //阻塞线程
        setBlocker(t, null);  //线程解除阻塞,清空blocker信息
    }

    public static void unpark(Thread thread) {
        if (thread != null)
            unsafe.unpark(thread);   //解除线程阻塞
    }

  这是Unsafe类中实质阻塞线程及解除线程阻塞的native函数,var1指明时间为绝对时间还是相对时间,false为相对时间,var2意为解除阻塞时间,设置为0则只有当线程中断,或调用unpark函数时解除锁定,若不为0,则等待var2时间后也会自动解除阻塞,注意这里的时间单位为纳秒;当var1位true时,为绝对时间,var2的时间单位为毫秒

    public native void unpark(Object var1);

    public native void park(boolean var1, long var2);

  最多等待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);
        }
    }

  

时间: 2024-08-05 12:06:59

JAVA并发--LockSupport的相关文章

java并发LockSupport

java并发LockSupport LockSupport是阻塞和唤醒线程的重要类. park()方法使得当前线程阻塞 unpark(Thread thread)唤醒线程 例子 可以把注释取消再执行,就会发现park()方法使得当前线程阻塞会使得main线程阻塞,无法结束. package com.java.javabase.thread.base.concurrent.lock; import lombok.extern.slf4j.Slf4j; import java.util.concur

【Java并发编程实战】—– AQS(三):阻塞、唤醒:LockSupport

在上篇博客([Java并发编程实战]-– AQS(二):获取锁.释放锁)中提到,当一个线程加入到CLH队列中时,如果不是头节点是需要判断该节点是否需要挂起:在释放锁后,需要唤醒该线程的继任节点 lock方法,在调用acquireQueued(): if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; 在acquireQueued()中调用parkAndCheckIn

JAVA并发编程J.U.C学习总结

前言 学习了一段时间J.U.C,打算做个小结,个人感觉总结还是非常重要,要不然总感觉知识点零零散散的. 有错误也欢迎指正,大家共同进步: 另外,转载请注明链接,写篇文章不容易啊,http://www.cnblogs.com/chenpi/p/5614290.html 本文目录如下,基本上涵盖了J.U.C的主要内容: JSR 166及J.U.C Executor框架(线程池. Callable .Future) AbstractQueuedSynchronizer(AQS框架) Locks & C

Java 并发基础

Java 并发基础 线程简述 线程是进程的执行部分,用来完成一定的任务; 线程拥有自己的堆栈,程序计数器和自己的局部变量,但不拥有系统资源, 他与其他线程共享父进程的共享资源及部分运行时环境,因此编程时需要小心,确保线程不会妨碍同一进程中的其他线程; 多线程优势 进程之间不能共享内存,但线程之间共享内存/文件描述符/进程状态非常容易. 系统创建进程时需要为该其分配很多系统资源(如进程控制块),但创建线程的开销要小得多,因此线程实现多任务并发比进程效率高. Java语言内置多线程支持,而不是单纯采

Java并发基础框架AbstractQueuedSynchronizer初探(ReentrantLock的实现分析)

AbstractQueuedSynchronizer是实现Java并发类库的一个基础框架,Java中的各种锁(RenentrantLock, ReentrantReadWriteLock)以及同步工具类(Semaphore, CountDownLatch)等很多都是基于AbstractQueuedSynchronizer实现的.AbstractQueuedSynchronizer 一般简称AQS,Abstract表示他是一个抽象类,Queued表示他是基于先进先出 FIFO 等待队列实现的,Sy

【死磕Java并发】-----J.U.C之AQS:阻塞和唤醒线程

此篇博客所有源码均来自JDK 1.8 在线程获取同步状态时如果获取失败,则加入CLH同步队列,通过通过自旋的方式不断获取同步状态,但是在自旋的过程中则需要判断当前线程是否需要阻塞,其主要方法在acquireQueued(): if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; 通过这段代码我们可以看到,在获取同步状态失败后,线程并不是立马进行阻塞,需要检查该线程的

Java并发编程与技术内幕:聊聊锁的技术内幕(上)

林炳文Evankaka原创作品.转载请注明出处http://blog.csdn.net/evankaka 一.基础知识 在Java并发编程里头,锁是一个非常重要的概念.就如同现实生活一样,如果房子上了锁.别人就进不去.Java里头如果一段代码取得了一个锁,其它地方再想去这个锁(或者再执行这个相同的代码)就都得等待锁释放.锁其实分成非常多.比如有互斥锁.读写锁.乐观锁.悲观锁.自旋锁.公平锁.非公平锁等.包括信号量其实都可以认为是一个锁. 1.什么时需要锁呢? 其实非常多的场景,如共享实例变量.共

Java的LockSupport.park()实现分析(转载)

LockSupport类是Java6(JSR166-JUC)引入的一个类,提供了基本的线程同步原语.LockSupport实际上是调用了Unsafe类里的函数,归结到Unsafe里,只有两个函数: 1 public native void unpark(Thread jthread); 2 public native void park(boolean isAbsolute, long time); isAbsolute参数是指明时间是绝对的,还是相对的. 仅仅两个简单的接口,就为上层提供了强大

Java并发编程深入学习

基本概念 在实践中,为了更好的利用资源提高系统整体的吞吐量,会选择并发编程.但由于上下文切换和死锁等问题,并发编程不一定能提高性能,因此如何合理的进行并发编程时本文的重点,接下来介绍关于锁最基本的一些知识(选学). volatile:轻量,保证共享变量的可见性,使得多个线程对共享变量的变更都能及时获取到.其包括两个子过程,将当前处理器缓存行的数据写回到系统内存,之后会使其他CPU里缓存了该内存地址的数据无效. synchronized:相对重量,其包含3种形式,针对普通同步方法,锁是当前实例对象