我学JUC之LockSupport

来源:互联网 发布:2017网络事件 编辑:程序博客网 时间:2024/06/06 03:13

LockSupport是基本的线程阻塞原语, 用于构建lock和其他同步类.

这个类将每个线程与一个permit进行关联(类似于java.util.concurrent.Semaphore一样的感觉). 如果permit可用的话, 调用park方法会消费掉permit, 然后直接返回. 否则, 会阻塞线程. 调用unpark会使得不可用的permit可用.(和Semaphore不同的是, permit不会累加, 每个线程最多只有一个permit).

因为使用了permit, park和unpark方法提供了有效的block和unblock线程的方法. 与已经废弃的Thread.suspend和Thread.resume方法不同. 线程A调用park, 另一个线程B调用unpark来unblock A, 这两者发生竞争, 仍然会使得线程A保持活性.

另外, 如果调用者线程被interrupted, park会返回, 同时park也有timeout支持. park方法可能会随时无原因的返回, 所以一般调用需要在一个检测condition是否可用的循环中使用. 在这个意义上, park可以当作是一个busy wait, 而不需要太多时间自旋.

三种形式的park都支持传入blocker参数. 这个对象在线程被阻塞等待permit时被记录下来, 可用于监控和诊断工具来分析当前线程因为何种原因而阻塞(这些工具通过使用getBlocker(Thread)方法来获取). 推荐使用这种能够传入bolcker对象参数的方法类型. 在lock实现中, 通常传入的blocker参数是this

这些方法同样也被设计用于创建更高层次的同步工具.
park方法被设计只用于以下的形式:

while (!canProceed()) { ... LockSupport.park(this); }}

在上面的代码中, park方法之前的, 无论是canProceed或其他方法都不应该涉及locking或block. 因为每个线程只有一个permit, 任何中间形式的使用park都会干扰想要的效果.
下面是一个简单的FIFO非重入锁的实现.

   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的方法:

//使用一个线程thread的permit可用. 如果这个thread之前阻塞在park方法上, 那么会unblock. 或者thread没有阻塞在park方法上, 下一次park方法时, thread会直接返回, 而不会阻塞. 不能保证线程没有启动时有效.public static void unpark(Thread thread)/**禁止程被调度, 除非permit可用如果permit可用, 那么就会被直接消费, 然后park调用返回. 否则当前线程会被停止调度, 保持休眠状态直到以下三个条件之一发生:1. 另外一个线程对其调用了unpark2. 另外一个线程调用了Thread.interrupt(thread)方法, interrupt当前线程3. 毫无理由的返回这个方法并不会报告什么原因造成了返回, 调用者应该立即检查造成thread调用park的Condition, 同时, 调用者也可能需要检查thread的interrupt状态.**/public static void park(Object blocker)//说明与上相同, nanos是线程阻塞的最长的纳秒数public static void parkNanos(Object blocker, long nanos)//说明同上. deadline是线程要阻塞到的绝对时间, Epoch时间戳, 单位为毫秒public static void parkUntil(Object blocker, long deadline)public static void park()public static void parkNanos(long nanos)public static void parkUntil(long deadline)//获取某个Thread最近调用park时传入的blocker. 每次可能返回不同的值public static Object getBlocker(Thread t)//设置一个线程的blocker对象private static void setBlocker(Thread t, Object arg)

blocker的原理:

设置blocker时设置的是Thread的一个字段parkBlocker, 通过UNSAFE类来获取其在Thread类中的偏移, 然后通过UNSAFE类在这个偏移上放置blocker的指针就可以了.

    static {        try {            UNSAFE = sun.misc.Unsafe.getUnsafe();            Class<?> tk = Thread.class;            //通过UNSAFE类来获取Thread类中的parkBlocker在内存中在对象中的偏移            parkBlockerOffset = UNSAFE.objectFieldOffset                (tk.getDeclaredField("parkBlocker"));            .....        } catch (Exception ex) { throw new Error(ex); }    }    public static void park(Object blocker) {        Thread t = Thread.currentThread();        setBlocker(t, blocker);        UNSAFE.park(false, 0L);        setBlocker(t, null);    }    private static void setBlocker(Thread t, Object arg) {        //使用UNSAFE的方法在对象的相应偏移位置放置对象指针        UNSAFE.putObject(t, parkBlockerOffset, arg);    }
0 0
原创粉丝点击