博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
LockSupport工具使用和源码分析
阅读量:4149 次
发布时间:2019-05-25

本文共 2900 字,大约阅读时间需要 9 分钟。

简述:在前面几篇分析java并发工具的博客当中,基于AQS实现的,底层都会用到LockSupport所提供的park和unpark(Thread)方法来进行线程的挂起和唤醒,所以这篇文章我准备来看下LockSupport到底是如何实现线程挂起和唤醒的,以及它有哪些比较好的功能,可以在以后开发当中使用。

我们知道在java当中我们可以使用Object类当中的wait方法和notify方法来实现线程的阻塞和唤醒,但是有一点,Object类中提供的wait和notify必须是在synchronized关键字加锁之后,才能使用。但是我们在使用AQS时候,并不使用synchronized关键字。LockSupport当中提供的park和unpark方法对线程的操作就可以满足。

除此之外我们来看一个例子

public class NotifyTest {    public static void main(String[] args)throws Exception {        final Object obj = new Object();        Thread A = new Thread(new Runnable() {            @Override            public void run() {                int sum = 0;                for(int i=0;i<10;i++){                    sum+=i;                }                try {                    synchronized (obj){                        obj.wait();                    }                }catch (Exception e){                    e.printStackTrace();                }                System.out.println(sum);            }        });        A.start();        //睡眠一秒钟,保证线程A已经计算完成,阻塞在wait方法        //Thread.sleep(1000);        synchronized (obj){            obj.notify();        }    }}

上述代码在我本地执行过几次,发现有时候可以打印出来计算结果,有时候程序一直不能结束,处于死循环当中,这是为什么呢,原因在线程启动之后调用了wait方法,main线程调用了notify方法,有种可能就是main线程调用notify方法先与线程调用wait方法,这个时候程序就一直无法执行。当我们把上述obj.wait方法和obj.notify方法替换成Lock.park和Lock.unpar(A)程序无论如何都可以正常执行成功。

综上所述,Object类当中线程挂起和唤醒和LockSupport中线程挂起和唤醒的不同点在于,Object类当中必须要在同步代码块里,而LockSupport则不需要,还有一点,Object当中如果先执行了notify在执行wait,那么线程将永远处于挂起不会被唤醒状态,LockSupport则不会。

接下来是关于源码分析

LockSupport中使用了大量Unsafe类来实现,我们先来看关于常用的几个方法

public class LockSupport {//挂起当前线程public static void park(){}// 挂起当前线程一定时间,超过某个时间后唤醒public static void parkNanos(long nanos){}// 挂起当前线程,直到deadline 之后唤醒 从1970年开始到deadline的毫秒数public static void parkUntil(long deadline){}// 挂起当前线程,阻塞对象public static void park(Object blocker){}// 挂起当前线程,在某个阻塞对象上,最长不超过nanos纳秒public static void parkNanos(Object blocker, long nanos) {}// 挂起当前线程在某个阻塞对象上,直到deadline时间public static void parkUntil(Object blocker, long deadline){}//唤醒挂起的线程public static void unpark(Thread thread){}}

其实以上挂起的方法主要是挂起指定时间,还有一个就是在某个对象上挂起指定时间,我们来看下具体的实现

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

对于park方法,直接挂起,等到unpark唤醒

对于park在某个对象上的实现

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

1、获取当前线程

2、设置线程的blocker对象(使用Unsafe的方式修改thread对象当中parkBlocker的值)

3、挂起线程

4、将当前对象的blocker对象清空

为什么要设置两次blocker?

在执行完Unsafe.park方法后,线程被挂起,不会往下面执行,这时候要等待Unsafe.unpark方法唤醒,然后再继续执行,如果unsafe.unpark方法执行后,该线程又执行了park方法,并且没有设置parkBlocer的值,我们拿到的就是之前park时,设置过得值,显然是不对的。

blocker的作用是什么?

其实在最早的时候,LockSupport提供的park方法,只有park(long) park(deadline) 和park,是没有设置blocker的方法的,主要是为了调试,在dump查看阻塞线程信息的时候,是没有的,之前我们使用synchronized阻塞在一个对象上的时候,是可以看到线程的阻塞对象,方便定位问题,LockSupport之前不提供blocker时,并不知道阻塞在哪个对象上了,因此jdk1.6提供了parkObject的方法。

以上就是关于LockSupport的介绍,如有问题,欢迎指正~

转载地址:http://ylvti.baihongyu.com/

你可能感兴趣的文章
数组中累加和为定值K的最长子数组长度
查看>>
素数对--腾讯2017校招编程
查看>>
JAVA集合--ArrayList实现原理
查看>>
synchronized与Lock
查看>>
数据库索引
查看>>
实现包含min,max,push,pop函数的栈
查看>>
实验2-6 字符型数据的输入输出
查看>>
实验3-5 编程初步
查看>>
实验4-1 逻辑量的编码和关系操作符
查看>>
实验5-2 for循环结构
查看>>
实验5-3 break语句和continue语句
查看>>
实验5-4 循环的嵌套
查看>>
实验5-5 循环的合并
查看>>
实验5-6 do-while循环结构
查看>>
实验5-7 程序调试入门
查看>>
实验5-8 综合练习
查看>>
第2章实验补充C语言中如何计算补码
查看>>
深入入门正则表达式(java) - 命名捕获
查看>>
使用bash解析xml
查看>>
android系统提供的常用命令行工具
查看>>