LockSupport

Posted greys

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LockSupport相关的知识,希望对你有一定的参考价值。

 

 

1、为什么想着了解LockSupport ?

因为 LockSupport 在底层的使用较多,它比内置锁( synchronized 隐式锁)使用起来更方便。通常使用的“等待——通知”有3种 (还可通过Atomic类实现):

  (1) ReetrantLock 提供了一个 newCondition 方法 获取到 ConditionObject对象,通过condition, 我们可以调用 await() 和 signal 方法;

      (2) 通过调用Object的 wait 和 notify 方法。 wait和notifyAll必须写在同步代码块中,而同步一般用 synchronized ,JDK1.8及更高的版本也推荐使用 synchronized ;

      (3) 通过 LockSupport 调用 park 和 unpark 方法;

  具体什么情况下使用什么方式,需我们充分了解 这3种方式的优缺点。

 

通过第2种方式实现:

public static void main(String[] args) throws InterruptedException {
        final Object obj = new Object();    
        Thread son = new Thread(new Runnable() {
            public void run() {
                try {
                    synchronized (obj) {
                        obj.wait();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("son thread");
            }
        });
        son.start();
        Thread.sleep(100); //阻塞wait方法
        synchronized (obj) {
            obj.notifyAll();
        }
    }

缺点:当我们注释掉sleep后,可能线程son一直处于运行中,因为son线程不是后台线程。

 

通过第3种方式实现:

public static void main(String[] args) throws InterruptedException {
        Thread son = new Thread(new Runnable() {
            @Override
            public void run() {
                LockSupport.park();
                System.out.println("son thread");
            }
        });
        son.start();
        Thread.sleep(100);   
        LockSupport.unpark(son);
    }

通过测试,主线程不执行sleep,son线程也会正常执行退出。这就是LockSupport的灵活性。

 

2、jdk中哪些类使用了LockSupport 

    (1)我们在实现一个线程时,如果我们需要线程返回结果,那么我们要实现callable。我们通过Future取到返回值。

public static void main(String[] args) throws InterruptedException, ExecutionException {
        ArrayBlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(100);
        ThreadPoolExecutor pool = new ThreadPoolExecutor(4, 4, 100, TimeUnit.SECONDS, queue);
        
        Future<String> future = pool.submit(new Callable<String>() {
            @Override
            public String call() throws Exception {
                TimeUnit.MILLISECONDS.sleep(100);
                return UUID.randomUUID().toString();
            }
        });
        String result = future.get();  
        System.out.println(result);
    }

上面一段代码,当我们在使用future.get时,如果线程还没执行完,那么程序是不是有问题? 还是它会阻塞,等到线程执行完?

通过查看源码,可以找到结果。

技术图片

继续深入awaitDone方法

技术图片

可以看到awaitDone调用了LockSupport方法实现阻塞效果。那么线程怎么唤醒阻塞的线程了???  当然是通过 run() 方法,猜想run执行完之后,通知等待线程。

技术图片

查看set方法:

技术图片

查看finishCompletion方法:

技术图片

可以看到通知是用的unpark. 

  (2)上面main函数中使用了同步容器,其实jdk中容器都包含一个对象 Condition , Condition是个接口,它的实现类为ConditionObject。同步容器中维护了2个condition:notEmpty和notFull,调用await(),实际上就是ConditionObject的await()方法。

技术图片

await()

技术图片

另外:ConditionObject是 AbstractQueuedSynchronizer (AQS)的一个内部类。

 

 3、LockSupport源码

  lockSupport方法中调用的是 UNSAFE的native方法,需要openJDK查看。

技术图片

技术图片

技术图片

技术图片

技术图片

 

park底层代码:

 技术图片

 

以上是关于LockSupport的主要内容,如果未能解决你的问题,请参考以下文章

LockSupport 工具相关整理

多线程同步之LockSupport

多线程同步之LockSupport

并发编程 - LockSupport 应用

LockSupport源码分析

13.LockSupport工具