`
MauerSu
  • 浏览: 513635 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

java.util.concurrent.locks.LockSupport

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

上源代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
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中有两个私有的成员变量:

?
1
2
    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类的实现:

?
1
2
3
4
5
6
7
8
    //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:偏移量又是做什么的?


?
1
2
3
4
5
6
    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)

?
1
2
3
4
    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)

?
1
2
3
    public static Object getBlocker(Thread t) {
        return unsafe.getObjectVolatile(t, parkBlockerOffset);
    }
参数:Thread t, 被操作的线程对象

返回:parkBlocker对象

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



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



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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
        /** 
         * 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)

?
1
2
3
4
    public static void unpark(Thread thread) {
        if (thread != null)
            unsafe.unpark(thread);
    }
参数:Thread thread, 需要被中止挂起的线程



带blocker参数的park方法

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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个条件

当配对的unpark发生或者

配对的unpark已经发生或者线程被中断时恢复(unpark先行,再执行park)

当absolute是false时,如果给定的时间是非0(负数)或者给定的时间(正数, 时间单位时毫秒)已经过去了(0的时候会一直阻塞着)。

当Absolute是true时,如果给定的时间(时间单位是纳秒)过去了或者伪造的(在我理解是参数不合法时)线程会恢复中断。


不带blocker参数的park方法

?
1
2
3
4
5
6
7
8
9
10
11
12
    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") ;

         }

     }
分享到:
评论

相关推荐

    Tomcat启动失败的问题排查与解决

    最近在工作中遇到一个问题,在发布失败的机器上Tomcat一直没有启动成功,所以只能想办法排查解决,下面这篇文章就介绍了在Tomcat启动时可能遇到的问题排查与解决方法,需要的朋友可以参考借鉴,下面来一起看看吧。

    Java 多线程与并发(3-26)-Java 并发 - Java中所有的锁.pdf

    在Java中,`java.util.concurrent.locks.LockSupport.parkNanos()`和`LockSupport.parkUntil()`方法可以用来实现自旋锁。`ReentrantLock`内部就使用了自旋锁策略,它会根据当前系统环境和锁的状态决定是否进行自旋。...

    synchronized.pdf

    `java.util.concurrent.locks.LockSupport`是低级别的线程阻塞和唤醒工具,它可以实现比`synchronized`更底层的线程控制,通常用于自定义锁实现。 综上所述,`synchronized`是Java多线程编程中不可或缺的关键字,...

    Java 多线程共享模型之管程(下).doc

    `park()`和`unpark()`是`java.util.concurrent.locks.LockSupport`类中的方法,它们提供了一种低级别的线程阻塞和唤醒机制。`park()`可以使当前线程暂停执行,直到被`unpark()`或者其他外部因素唤醒。`unpark()`则...

    Java中LockSupport的使用.docx

    Java中的LockSupport是一个底层工具类,它在JDK 1.6版本中被引入到`java.util.concurrent.locks`包中,主要用于构建锁和其他同步工具类。LockSupport的核心功能是提供线程阻塞和唤醒的原语,这些功能在Java的并发...

    java 中死锁问题的实例详解

    此外,JDK 1.6引入了`java.util.concurrent.locks.LockSupport.park()`和`LockSupport.unpark(Thread thread)`方法,可以用来实现更细粒度的控制,帮助解决死锁问题。 4. 设置超时:在获取锁时设置超时时间,如果在...

    Java 多线程与并发(9-26)-JUC锁- LockSupport详解.pdf

    这个类位于java.util.concurrent.locks包下,是实现并发编程中AQS(AbstractQueuedSynchronizer)框架的重要基础之一。LockSupport的目的是为了简化锁和其他同步类的实现,允许开发者创建更加高级的并发工具。 ...

    Java Thread Programming

    书中会讲解如何识别和避免这些问题,以及如何使用`java.util.concurrent.locks`包中的`LockSupport`类来实现精细的线程控制。 除了理论知识,实战经验同样重要。`JTP_Source.zip`可能包含了示例代码和练习,读者...

    如何聪明地使用锁

    在Java中,`java.util.concurrent.locks.Lock`接口及其实现如`ReentrantLock`提供了互斥锁的功能。 3. **读写锁(Read-Write Lock)**: 读写锁允许多个线程同时读取数据,但只允许一个线程进行写操作。这样可以...

    Java并发锁简介-动力节点共9页.pdf.zip

    2. **可重入锁(ReentrantLock)**:这是Java并发包`java.util.concurrent.locks`中的一个锁,相比内置锁具有更高的灵活性。可重入锁支持公平锁和非公平锁模式,允许锁的持有者再次获取锁(即重入),并且可以实现锁...

    Java实监控同步的代码

    5. ** java.util.concurrent.locks.Condition**:Condition接口提供了比synchronized更细粒度的控制,可以创建多个条件变量,每个条件变量对应一个等待队列,线程可以在满足特定条件时才被唤醒。 6. ** Phaser 同步...

    LockSupportTester.zip

    LockSupport是Java并发编程中的一个重要工具类,位于`java.util.concurrent.locks`包下,它提供了线程阻塞和唤醒的底层支持。在Java多线程编程中,LockSupport主要用于构建锁和其他同步机制,比如ReentrantLock和...

    java多线程之线程同步七种方式代码示例

    `java.util.concurrent.locks.ReentrantLock`提供了比`synchronized`更灵活的锁定机制,如公平锁、非公平锁、尝试加锁、定时等待等功能。它还可以实现更复杂的锁,如读写锁。 5. LockSupport.park() 和 LockSupport...

    浅析JAVA多线程.pdf

    Java 5.0版本中引入了java.util.concurrent.locks包,其中提供了LockSupport类,它是创建锁和其他同步类的基本线程阻塞原语。LockSupport类中的park方法用于阻塞当前线程,直到获取许可证;unpark方法用于释放指定...

    java的Lock锁原理详解.docx

    相反,Lock是Java并发包java.util.concurrent.locks中的接口,它提供了更细粒度的锁控制。Lock接口提供了比synchronized更丰富的功能,如尝试获取锁(tryLock)、可中断的锁获取(lockInterruptibly)、超时获取锁...

    java线程阻塞中断与LockSupport使用介绍

    `Thread.interrupt()` 方法是用于向线程发送中断请求,而`LockSupport` 是Java 5引入的一个低级别的线程同步工具,提供了比`synchronized` 和 `java.util.concurrent.locks` 包更细粒度的控制。下面我们将详细讨论这...

    Java并发包源码分析(JDK1.8)

    Java并发包源码分析(JDK1.8):囊括了java.util.concurrent包中大部分类的源码分析,其中涉及automic包,locks包(AbstractQueuedSynchronizer、ReentrantLock、ReentrantReadWriteLock、LockSupport等),queue...

    Java锁之阻塞锁介绍和代码实例

    `LockSupport.park()`和`unpark()`方法是`java.util.concurrent`包中的低级同步工具,可以用来创建自定义的锁或者其他同步组件,`park()`方法可以使当前线程阻塞,`unpark()`方法可以解除一个线程的阻塞状态。...

    android demo,locksuport的unpark一个线程。

    在Android开发中,`LockSupport` 是一个非常重要的工具类,它属于并发处理包 `java.util.concurrent.locks`。这个类提供了低级别的线程阻塞和唤醒机制,可以帮助开发者实现复杂的线程同步策略。在标题和描述中提到的...

    java+多线程+同步详解Java源码

    - `java.util.concurrent.locks.Lock`接口提供了比`synchronized`更灵活的锁操作,如可重入、公平性、尝试加锁、定时加锁等。 - `ReentrantLock`是Lock接口的实现,具有可重入性,其功能类似于`synchronized`,但...

Global site tag (gtag.js) - Google Analytics