java.util.concurrent.locks.LockSupport (讲得比较细)

转自:   https://my.oschina.net/readjava/blog/282882

摘要: 要学习JAVA中是如何实现线程间的锁,就得从LockSupport这个类先说起,因为这个类实现了底层的一些方法,各种的锁实现都是这个基础上发展而 来的。这个类方法很少,但理解起来需要花费一点时间,因为涉及了很多底层的知识,这些都是我们平时不关心的。

要学习JAVA中是如何实现线程间的锁,就得从LockSupport这个类先说起,因为这个类实现了底层的一些方法,各种的锁实现都是这个基础上发展而来的。这个类方法很少,但理解起来需要花费一点时间,因为涉及了很多底层的知识,这些都是我们平时不关心的。

上源代码:

package java.util.concurrent.locks;
import java.util.concurrent.*;
import sun.misc.Unsafe;

public class LockSupport {
    private LockSupport() {} // Cannot be instantiated.

    // Hotspot implementation via intrinsics API
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long parkBlockerOffset;

    static {
        try {
            parkBlockerOffset = unsafe.objectFieldOffset
                (java.lang.Thread.class.getDeclaredField("parkBlocker"));
        } catch (Exception ex) { throw new Error(ex); }
    }

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

    public static void unpark(Thread thread) {
        if (thread != null)
            unsafe.unpark(thread);
    }

    public static void park(Object blocker) {
        Thread t = Thread.currentThread();
        setBlocker(t, blocker);
        unsafe.park(false, 0L);
        setBlocker(t, null);
    }

    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);
        }
    }

    public static void parkUntil(Object blocker, long deadline) {
        Thread t = Thread.currentThread();
        setBlocker(t, blocker);
        unsafe.park(true, deadline);
        setBlocker(t, null);
    }

    public static Object getBlocker(Thread t) {
        return unsafe.getObjectVolatile(t, parkBlockerOffset);
    }

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

    public static void parkNanos(long nanos) {
        if (nanos > 0)
            unsafe.park(false, nanos);
    }

    public static void parkUntil(long deadline) {
        unsafe.park(true, deadline);
    }
}

这个类提供的都是静态方法,且无法被实例化。

在LockSupport中有两个私有的成员变量:

    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long parkBlockerOffset;

大家都知道JAVA语言是平台无关的,一次编译,可以在任何平台上运行,但是如果真的不可以调用一些平台相关的方法吗?其实unsafe类是可以做到的。

unsafe:是JDK内部用的工具类。它通过暴露一些Java意义上说“不安全”的功能给Java层代码,来让JDK能够更多的使用Java代码来实现一些原本是平台相关的、需要使用native语言(例如C或C++)才可以实现的功能。该类不应该在JDK核心类库之外使用。

parkBlokcerOffset:parkBlocker的偏移量,从字面上理解是这么个东东。但是parkBlocker又是干嘛的?偏移量又是做什么的呢?让我们来看看Thread类的实现:

    //java.lang.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;

问题1:parkBlocker又是干嘛的?

原来java.lang.Thread的实现当中有这么一个对象。从注释上看,这个对象被LockSupport的setBlocker和getBlocker调用。查看JAVADOC会发现这么一段解释:

大致意思是,这个对象是用来记录线程被阻塞时被谁阻塞的。用于线程监控和分析工具来定位原因的。主要调用了LockSupport的getBlocker方法。

原来,parkBlocker是用于记录线程是被谁阻塞的。可以通过LockSupport的getBlocker获取到阻塞的对象。用于监控和分析线程用的。

问题2:偏移量又是做什么的?

    static {
        try {
            parkBlockerOffset = unsafe.objectFieldOffset
                (java.lang.Thread.class.getDeclaredField("parkBlocker"));
        } catch (Exception ex) { throw new Error(ex); }
    }

这个要往后看一下,原来偏移量就算Thread这个类里面变量parkBlocker在内存中的偏移量。

JVM的实现可以自由选择如何实现Java对象的“布局”,也就是在内存里Java对象的各个部分放在哪里,包括对象的实例字段和一些元数据之 类。sun.misc.Unsafe里关于对象字段访问的方法把对象布局抽象出来,它提供了objectFieldOffset()方法用于获取某个字段 相对Java对象的“起始地址”的偏移量,也提供了getInt、getLong、getObject之类的方法可以使用前面获取的偏移量来访问某个 Java对象的某个字段。

问题3:为什么要用偏移量来获取对象?干吗不要直接写个get,set方法。多简单?

仔细想想就能明白,这个parkBlocker就是在线程处于阻塞的情况下才会被赋值。线程都已经阻塞了,如果不通过这种内存的方法,而是直接调用线程内的方法,线程是不会回应调用的。

private static void setBlocker(Thread t, Object 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);
    }

参数:

  • Thread t 需要被赋值Blocker的线程
  • Object arg 具体的Blocker对象

解读:有了之前的理解,这个方法就很好理解了。对给定线程t的parkBlocker赋值。为了防止,这个parkBlocker被误用,该方法是不对外公开的。

public static Object getBlocker(Thread t)

    public static Object getBlocker(Thread t) {
        return unsafe.getObjectVolatile(t, parkBlockerOffset);
    }

参数:Thread t, 被操作的线程对象

返回:parkBlocker对象

解读:从线程t中获取他的parkerBlocker对象。这个方法是对外公开的。

是不是可以利用这个方法来写一个监控程序,炫耀一把.

再讲其他几个方法之前,先谈谈park和unpark是做什么的.

看看SUN的官方解释 (点击查看源码

        /**  
         * Unblock the given thread blocked on <tt>park</tt>, or, if it is
         * not blocked, cause the subsequent call to <tt>park</tt> not to
         * block.  Note: this operation is "unsafe" solely because the
         * caller must somehow ensure that the thread has not been
         * destroyed. Nothing special is usually required to ensure this
         * when called from Java (in which there will ordinarily be a live
         * reference to the thread) but this is not nearly-automatically
         * so when calling from native code.
         * @param thread the thread to unpark.
         * 
         */
         public native void unpark(Object thread);
     
        /**
         * Block current thread, returning when a balancing
         * <tt>unpark</tt> occurs, or a balancing <tt>unpark</tt> has
         * already occurred, or the thread is interrupted, or, if not
         * absolute and time is not zero, the given time nanoseconds have
         * elapsed, or if absolute, the given deadline in milliseconds
         * since Epoch has passed, or spuriously (i.e., returning for no  
          * "reason"). Note: This operation is in the Unsafe class only
         * because <tt>unpark</tt> is, so it would be strange to place it
         * elsewhere.
         */
         public native void park(boolean isAbsolute, long time);

字面理解park,就算占住,停车的时候不就把这个车位给占住了么?起这个名字还是很形象的。unpark,占住的反义词,就是释放。把车从车位上开走。

翻译一下:

  • park:阻塞当前线程,(1)当配对的unpark发生或者(2)配对的unpark已经发生或者线程被中断时恢复(unpark先 行,再执行park)。 (3)当absolute是false时,如果给定的时间是非0(负数)或者给定的时间(正数, 时间单位时毫秒)已经过去了(0的时候会一直阻塞着)。(4)当Absolute是true时,如果给定的时间(时间单位是纳秒)过去了或者伪造的(在我 理解是参数不合法时)线程会恢复中断。这个操作是不安全的,所以在其他调用会很奇怪(奇怪?反正就是用的时候要小心)
    • unpark:当指定线程被park命令阻塞时unpark命令可以恢复阻塞。在park命令没有被先调用过的时候,调用unpark,线程仍然不被阻塞。(翻译的有点那个...).

    理解一下,park与unpark命令是成对出现的。unpark必须要在park命令后执行。但是线程的恢复并不一定要用unpark, 因为park的时间参数,有些情况下线程会自己恢复。

    public static void unpark(Thread thread)

        public static void unpark(Thread thread) {
            if (thread != null)
                unsafe.unpark(thread);
        }

    参数:Thread thread, 需要被中止挂起的线程

    带blocker参数的park方法

    public static void park(Object blocker) {
            Thread t = Thread.currentThread();
            setBlocker(t, blocker);
            unsafe.park(false, 0L);
            setBlocker(t, null);
        }
    
        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);
            }
        }
    
        public static void parkUntil(Object blocker, long deadline) {
            Thread t = Thread.currentThread();
            setBlocker(t, blocker);
            unsafe.park(true, deadline);
            setBlocker(t, null);
        }

    参数:

    • Object blocker:用于记录到线程中的parkBlocker对象。
    • nanos:在nanos时间后线程自动恢复挂起
    • deadline:在deadline时刻线程自动(这个毫秒其实就是自1970年1月1日0时起的毫秒数)

    解读:这三个方法其实是一个意思,把blocker放到线程当中,注意,这个park方法是一个阻塞的方法,除非4个条件

    1. 当配对的unpark发生或者
    2. 配对的unpark已经发生或者线程被中断时恢复(unpark先行,再执行park)
    3. 当absolute是false时,如果给定的时间是非0(负数)或者给定的时间(正数, 时间单位时毫秒)已经过去了(0的时候会一直阻塞着)。
    4. 当Absolute是true时,如果给定的时间(时间单位是纳秒)过去了或者伪造的(在我理解是参数不合法时)线程会恢复中断。

    不带blocker参数的park方法

        public static void park() {
            unsafe.park(false, 0L);
        }
    
        public static void parkNanos(long nanos) {
            if (nanos > 0)
                unsafe.park(false, nanos);
        }
    
        public static void parkUntil(long deadline) {
            unsafe.park(true, deadline);
        }

    这三个方法跟上面一样,唯一区别是没有做parkBlocker的赋值操作。



    来自我同事的并发编程网:

    我们继续看一下JVM是如何实现park方法的,park在不同的操作 系统使用不同的方式实现,在linux下是使用的是系统方法pthread_cond_wait实现。实现代码在JVM源码路径src/os/linux /vm/os_linux.cpp里的 os::PlatformEvent::park方法,代码如下:

    void os::PlatformEvent::park() {

    int v ;

    for (;;) {

    v = _Event ;

    if (Atomic::cmpxchg (v-1, &_Event, v) == v) break ;

    }

    guarantee (v >= 0, "invariant") ;

    if (v == 0) {

    // Do this the hard way by blocking ...

    int status = pthread_mutex_lock(_mutex);

    assert_status(status == 0, status, "mutex_lock");

    guarantee (_nParked == 0, "invariant") ;

    ++ _nParked ;

    while (_Event < 0) {

    status = pthread_cond_wait(_cond, _mutex);

    // for some reason, under 2.7 lwp_cond_wait() may return ETIME ...

    // Treat this the same as if the wait was interrupted

    if (status == ETIME) { status = EINTR; }

    assert_status(status == 0 || status == EINTR, status, "cond_wait");

    }

    -- _nParked ;

    // In theory we could move the ST of 0 into _Event past the unlock(),

    // but then we‘d need a MEMBAR after the ST.

    _Event = 0 ;

    status = pthread_mutex_unlock(_mutex);

    assert_status(status == 0, status, "mutex_unlock");

    }

    guarantee (_Event >= 0, "invariant") ;

    }

    }

时间: 2024-11-08 05:05:18

java.util.concurrent.locks.LockSupport (讲得比较细)的相关文章

java多线程常见类与方法之java.util.concurrent.locks

1.java.util.concurrent.locks包里面的主要接口Lock,Condition,ReadWriteLock,主要的类为ReentrantLock,ReentrantReadWriteLock,ReentrantReadWriteLock.ReadLock,ReentrantReadWriteLock.WriteLock,LockSupport 2.ReentrantLock里面的主要方法为:lock(),unlock(),tryLock(),lockInterruptibl

Java学习笔记—多线程(java.util.concurrent.locks包,转载)

一.synchronized的缺陷 synchronized是java中的一个关键字,也就是说是Java语言内置的特性.那么为什么会出现Lock呢? 如果一个代码块被synchronized修饰了,当一个线程获取了对应的锁,并执行该代码块时,其他线程便只能一直等待,等待获取锁的线程释放锁,而这里获取锁的线程释放锁只会有两种情况: 1)获取锁的线程执行完了该代码块,然后线程释放对锁的占有: 2)线程执行发生异常,此时JVM会让线程自动释放锁. 那么如果这个获取锁的线程由于要等待IO或者其他原因(比

12、java5锁java.util.concurrent.locks.Lock之ReentrantLock

JDK文档描述: public interface LockLock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作.此实现允许更灵活的结构,可以具有差别很大的属性,可以支持多个相关的 Condition 对象. 锁是控制多个线程对共享资源进行访问的工具.通常,锁提供了对共享资源的独占访问.一次只能有一个线程获得锁,对共享资源的所有访问都需要首先获得锁.不过,某些锁可能允许对共享资源并发访问,如 ReadWriteLock 的读取锁. synchronized 方

synchronized/java.util.concurrent.locks.Loc/如何格式化日期/将字符“12345”转换成long型

1.简述synchronized和java.util.concurrent.locks.Lock的异同?解答:主要相同点:Lock能完成synchronized所实现的所有功能 主要不同点:Lock有比synchronized更精确的线程语义和更好的性能.synchronized会自动释放锁,而Lock一定要求程序员手工释放,并且必须在finally从句中释放. 2.如何格式化日期解答:Date now=new Date();SimpleDateFormat sdf=new SimpleDate

java.util.concurrent.locks lock锁【2】

Lock 锁介绍 JDK1.5以前,我们实现线程同步都是通过synchroized关键字进行方法或者语句块锁定,以保证该关键字作用域内的操作都是原子性操作. JDK1.5以后,提供的并发包提供了更强大的功能和更为灵活,最为关键的是需要手工释放锁,需要unlock必须在finally方法内.这是非常值得注意的事情. 介绍一下Lock接口.实现类有3个,分别是 普通锁,读写锁-写锁,读写锁-读锁. API文档给出了相近的说明和demo. Lock l = ...; l.lock(); try { /

简述synchronized和java.util.concurrent.locks.Lock异同

主要相同点:Lock能完成synchronized所实现的所有功能.主要不同点:Lock有比synchronized更精确的线程语义和更好的性能.syncronized会自动释放锁,而Lock一定要程序员手动释放,并且必须在finally从句中释放.Lock还有更强大的功能,例如它的tryLock()方法可以以非阻塞方式去拿锁. synchronized (lockObject) { // update object state }   Lock lock = new ReentrantLock

synchronized和java.util.concurrent.locks.Lock的区别

在看ConcurrentHashMap 源码的时候看到lock这个锁机制,不明白它和Synchronized的区别,查了一些资料记录下来,在Lock的文档中,对Lock的解释是:Lock实现比synchronized 提供了更多额外的锁操作,它有更灵活的结构,可以支持不同的属性,可以支持多个相关条件的对象.那下边看一下Lock可以提供哪些比synchronized 额外的操作,也就是解决synchronized 存在的问题: 1.它无法中断一个正在等候获得锁的线程 2.也无法通过投票得到锁,如果

java基础知识回顾之java Thread类学习(把)--java.util.concurrent.locks(JDK1.5)与synchronized异同讲解

看API文档介绍几个方法:  JDK1.5中提供了多线程的升级解决方案: 特点: 1.将同步synchronized显示的替换成Lock                    2.接口Condition:Condition替代了Object监视器方法(wait.notify.notifyAll),分别替换成了await(),signal() (唤醒一个等待线               程),signalAll() 唤醒多个线程.一个锁可以绑定多个condition对象,可以对应好几组wait,

java.util.concurrent.locks.Lock类的lock和lockInterruptibly方法的区别

什么是可中断的锁获取呢?就是:线程在等待获取锁的过程中,是否能够响应中断,以便在被中断的时候能够解除阻 塞状态,而不是傻傻地一直在等待.java对象的内置锁(synchronized)就是一种不可中断的锁,也就是说如果一个线 程在等待获取某个对象的内置锁,就算是该线程被其他线程中断,该线程仍然继续等待内置锁,而不是解除阻塞状 态,也不会抛出InterruptedException.Lock类的lock()类似synchronized,是不可中断的,在等待获取锁的过程中, 不响应中断请求:lock