Android LockSupport 分析

      我们以前在对线程进行阻塞与唤醒的时候经常会使用Object类中的wait()和notify(),其实除了这个方式之外,Java中还提供了另外一种的方式来对线程进行挂起和恢复:LockSupport。

案例演示

      该类中有两个非常核心的方法park()和unpark()方法分别用于对线程进行挂起和恢复的操作,我们知道如果对线程进行了挂起的操作的话,则当前线程处于等待的状态。下面我们来模拟一下:当一个线程的任务执行完成之后再接着执行第二个线程,这种情况在我们的平时的生活中是非常多见的。

public static void main(String[] args) {
    //现在的情况就是当线程1的任务执行完毕之后再接着执行线程2的任务。
    final Thread thread2 = new Thread(new Runnable() {
        @Override
        public void run() {
            //首先我们将线程2进行挂起,然后等线程1执行完成之后再恢复
            LockSupport.park();
            System.out.println("开始执行线程2任务");
            num = num + 9;
            System.out.println("Thread.." + Thread.currentThread().getName() + "...number..." + num);
        }
    });

    Thread thread1 = new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("开始执行线程1任务");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            num = num + 2;
            System.out.println("Thread.." + Thread.currentThread().getName() + "...number.." + num);
            //恢复线程2的执行。
            LockSupport.unpark(thread2);
        }
    });
    thread1.start();
    thread2.start();
}

      上面仅仅只是LockSupport的一个方面的例子实际应用起来还是比较简单的,我们只要记住park()是将当前的线程挂起,挂起之后该线程将无法进行继续执行任务的。unpark()是将某个线程进行唤醒能继续执行任务的,这个是比较好理解的。LockSupport有点类似于二元信号量(只有1个许可证可供使用),如果这个许可证还没有被占用,当前线程获取许可证继续执行;如果许可证已经被占用,当前线程阻塞,等待获取许可。

public static void main(String[] args) {
    LockSupport.park();
    System.out.println("该行文字将打印不出来");
}

    在运行上面的代码的时候主线程会被阻塞的,也就是说那个文字是不能打印出来的。我们也可以先释放许可,然后再获取许可,主线程就能够正常的终止了;LockSupport的许可一般来说是对应的,如果多次的unpark(),只有一次的park也不会出现什么问题;但是多次的unpark(),多次的park()就会有问题的。接下来再看看例子:

public static void main(String[] args) {
    LockSupport.unpark(Thread.currentThread());
    LockSupport.park();
    System.out.println("该行文字将打印出来");
}

    下面我们将多次的调用park()方法,但是只调用一次的unpark()方法来进行的操作。

public static void main(String[] args) {
    LockSupport.unpark(Thread.currentThread());
    LockSupport.park();
    System.out.println("该行文字将打印出来");
    //我们多次的调用了park(),但是我们只是调用了一次的unpark()
    LockSupport.park();
    System.out.println("该行文字将打印不出来");
}

原理分析

     LockSupport的底层最后还是使用了Unsafe.java去实现的park()和unpark(),对于Unsafe介绍我们在之前的博客是已经介绍过了,该类是Java并发中的核心,基本上所有的多线程的并发都是与该类有关的,下面我们来看看LockSupport的源代码:

public class LockSupport {

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

    // Hotspot implementation via intrinsics API
    private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
    private static final long PARKBLOCKER;
    private static final long SECONDARY;
    static {
        try {
            PARKBLOCKER = U.objectFieldOffset
                (Thread.class.getDeclaredField("parkBlocker"));
            SECONDARY = U.objectFieldOffset
                (Thread.class.getDeclaredField("threadLocalRandomSecondarySeed"));
        } catch (ReflectiveOperationException e) {
            throw new Error(e);
        }
    }

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

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

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

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

    从上面的代码中我们看到最后调用的还是Unsafe.java的park()和unpark()方法来实现的。下面我们就直接找到sun.misc包下的Unsafe类

public void park(boolean absolute, long time) {
    if (absolute) {
        Thread.currentThread().parkUntil$(time);
    } else {
        Thread.currentThread().parkFor$(time);
    }
}

public void unpark(Object obj) {
    if (obj instanceof Thread) {
        ((Thread) obj).unpark$();
    } else {
        throw new IllegalArgumentException("valid for Threads only");
    }
}

    当我们翻开Unsafe类中的park()方法和unpark()方法的时候,我们瞬间是不是觉得非常的吃惊?因为最后还是调用的Thread里面的一些方法。下面我们接着看Thread的方法。

public class Thread implements Runnable 
{
    private final Object lock = new Object();
    /** the park state of the thread */
    private int parkState = ParkState.UNPARKED;

    public final void parkFor$(long nanos) {
        synchronized(lock) {
            switch (parkState) {
                case ParkState.PREEMPTIVELY_UNPARKED: {
                    parkState = ParkState.UNPARKED;
                    break;
                }
                case ParkState.UNPARKED: {
                    long millis = nanos / NANOS_PER_MILLI;
                    nanos %= NANOS_PER_MILLI;
                    parkState = ParkState.PARKED;
                    try {
                        lock.wait(millis, (int) nanos);
                    } catch (InterruptedException ex) {
                        interrupt();
                    } finally {
                        /*
                         * Note: If parkState manages to become
                         * PREEMPTIVELY_UNPARKED before hitting this
                         * code, it should left in that state.
                         */
                        if (parkState == ParkState.PARKED) {
                            parkState = ParkState.UNPARKED;
                        }
                    }
                    break;
                }
                default /*parked*/: {
                    throw new AssertionError("Attempt to repark");
                }
            }
        }
    }

    public final void parkUntil$(long time) {
        synchronized(lock) {
            long delayMillis = time - System.currentTimeMillis();

            if (delayMillis <= 0) {
                parkState = ParkState.UNPARKED;
            } else {
                parkFor$(delayMillis * NANOS_PER_MILLI);
            }
        }
    }

    public final void unpark$() {
        synchronized(lock) {
            switch (parkState) {
                case ParkState.PREEMPTIVELY_UNPARKED: {
                    /*
                     * Nothing to do in this case: By definition, a
                     * preemptively unparked thread is to remain in
                     * the preemptively unparked state if it is told
                     * to unpark.
                     */
                    break;
                }
                case ParkState.UNPARKED: {
                    parkState = ParkState.PREEMPTIVELY_UNPARKED;
                    break;
                }
                default /*parked*/: {
                    parkState = ParkState.UNPARKED;
                    lock.notifyAll();
                    break;
                }
            }
        }
    }
}

    通过上面对Unsafe的代码追溯到Thread中的时候,我们能很明显的知道了Unsafe中的unpark和park()对线程的唤醒和挂起其实最后都是调用了线程本身中的unpark()和park()方法的。下面我们依次的来分析上述的几种情况:

  • 调用LockSupport.park()会阻塞当前的线程
    在上面Thread的park()方法中,我们可以很明显地看到初次使用park()方法的话,最后则会调用 ParkState.UNPARKED分支的,也就是说最后还是会调用Object.java的wait(long millis, int nanos)方法来挂起当前的线程中的。

  • 第一次调用unpark(),然后再调用park()不会当前的线程
    我们在Thread的unpark ()unpark()parkStateUNPARKEDparkStateParkState.PREEMPTIVELYUNPARKEDpark()parkStateUNPARKEDpark()UNPARKEDparkFor ()第二个分支中了,则最后还是调了Object.java的wait()去挂起当前的线程**

    综上所述发现LockSupport的park()和unpark()最后还是调用了Object的wait()和notifyAll()来达到对线程进行挂起和唤醒的操作的,只是人家把这些都封装的更加方便和实用了,不需要我们重复的去造轮子了。所以对线程的挂起和恢复最底层的还是在Object的wait()和notifyAll()中,其他的类更多的则是对它们的一个封装的。下面看看具体的方法调用:

image_1bsq8f14a1agnsf1nt9e9db9.png-74.4kB

总结

    通过我们对LockSupport的park()和unpark()方法分析发现其内部的原理最后还是调用了Object的wait()和notifyAll()来对线程进行挂起和恢复的。如果以后我们以后还需要继续深入底层去理解的话,则应该往Object的jni方法中去了解到。但是LockSupport的封装确实是非常的方便我们对线程进行挂起和恢复的操作的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值