咳嗽发烧吃什么药| 嗓子干痒是什么原因| 久旱逢甘露是什么意思| 伟哥叫什么| 乙肝小二阳是什么意思| FAN英语什么意思| 十五年是什么婚| 宫颈炎吃什么药最好| 舌苔发黑是什么原因引起的| 很困但是睡不着是什么原因| 一家之主是什么意思| 什么中药治肝病最好| may是什么意思| 月经要来之前有什么症状| 腹部胀气是什么原因| 正气是什么意思| 鳄鱼属于什么动物| 自缢死亡是什么意思| 今天会开什么生肖| 为什么会突然吐血| 双性人是什么意思| 老流鼻血是什么原因| 三焦热盛是什么意思| 河汉是什么意思| 什么鱼最好养| marmot什么牌子| 女生排卵期是什么意思| 6月12是什么星座| 92年属什么的生肖| 三什么一什么四字词语| 10086查话费发什么短信| 什么是痤疮图片| 什么是混合物| 剖腹产坐月子吃什么| 手脚抽筋是什么原因引起的| 什么叫卒中| 十二指肠溃疡吃什么药| 类风湿挂什么科室| 男孩什么时候开始发育| 深红色是什么颜色| 心肌缺血用什么药效果最好| 高尿酸血症吃什么药| 吃什么排铜最快| 什么是动物奶油| 天花是什么病| 4月1日什么星座| 什么男什么女的成语| 勃起是什么| 包子有什么馅的| 爱出汗的人是什么原因| 飞机后面的白烟是什么| 为什么会得毛囊炎| 里脊肉是什么肉| 红烧肉炖什么菜最好吃| 痛风不能吃什么水果| 海是什么生肖| wink是什么意思| gerd是什么病| opv是什么疫苗| 肝胆相照是什么生肖| 弦是什么| 失不出头念什么| 1970属什么| 肾结石要注意什么| 动物的脖子有什么作用| 芈月是秦始皇什么人| 腰间盘膨出和突出有什么区别| 负罪感什么意思| 荔枝不能与什么一起吃| 没有什么过不去| 血糯米是什么米| 神戳戳是什么意思| lmy是什么意思| 迪桑特属于什么档次| 为什么一年比一年热| 结膜炎角膜炎用什么眼药水| 什么是静脉血栓| 发票抬头是什么| 红苋菜不能和什么一起吃| 膝盖积液有什么症状| 十二月二十三是什么星座| 云南什么族| 内啡肽是什么| 肝钙化是什么意思| 脸上掉皮是什么原因| 眼睛经常长麦粒肿是什么原因| aut0是什么意思| 生理期吃什么水果| 昙花一现是什么生肖| 戴玉对身体有什么好处| 失眠是什么原因| 易烊千玺的爸爸是干什么的| 江苏龙虾盱眙读什么| 万足读什么| 血栓是什么| plg是什么意思| 有口无心是什么意思| 雷震子是什么神位| 动脉硬化是什么症状| 黄风怪是什么动物| 无花果吃了有什么好处| 感染hpv用什么药| 燕条和燕盏有什么区别| 谦虚的近义词是什么| 泡沫是什么材料做的| 三轮体空是什么意思| 3月是什么季节| 拉肚子吃什么药效果好| 番茄酱和番茄沙司有什么区别| 牙龈肿胀什么原因| 六月底是什么星座| 肾与性功能有什么关系| 白事随礼钱有什么讲究| 吉尼斯是什么意思| 吃什么能快速补血| 2月15是什么星座| 颈椎病吃什么药| 员级职称是什么意思| 肾结石可以吃什么食物| 贼头贼脑是什么生肖| 击剑什么意思| 变态是什么意思| 羽五行属什么| 乳腺是什么科| 什么病会引起腰疼| 华盖是什么意思| 软组织挫伤是什么意思| 为什么会出现彩虹| 重字五行属什么| 心脏彩超挂什么科| 肾病吃什么药最好| 姜维属什么生肖| 我还是什么| 也字五行属什么| 腋窝爱出汗是什么原因| 微信号为什么会封号| 鲶鱼效应是什么意思| 顺利是什么意思| 乳晕是什么意思| 1989是什么生肖| 脑梗做什么检查最准确| 鱼腥味是什么妇科病| 肺结核是什么| 吃羊肉不能和什么一起吃| 怀孕什么时候显怀| 命运多舛是什么意思| 命运是什么意思| 避孕药吃多了有什么副作用| 逆光是什么意思| 治疗白头发挂什么科| 贞操带是什么| 明天属什么生肖| 外阴瘙痒用什么洗| 流产后吃什么水果最佳| 排骨炖什么比较好吃| 六月是什么星座| 梦见水是什么征兆| 什么是阑尾炎| 睡觉为什么会打呼噜| 血糖能吃什么水果| 耳鸣什么原因引起| 卵巢囊性结构是什么| 结缡什么意思| 什么天空填动词| 分水岭是什么意思| 虾吃什么食物| 网调是什么意思| 什么情况下需要安装心脏起搏器| 宫颈多发囊肿是什么意思| 绿豆和什么相克中毒| 多多益善的益是什么意思| 五不遇时是什么意思| 全期猫粮什么意思| scc是什么检查项目| 10月24号是什么星座| 呵呵代表什么意思| 飞蚊症吃什么药| 低钾有什么症状和危害| 高血压高血糖能吃什么水果| ig什么意思| 血压高不能吃什么| 老鹰代表什么生肖| 1级高血压是什么意思| 感觉抑郁了去医院挂什么科| 夜不能寐是什么意思| 美容师都要学什么| 团县委是什么单位| 妇科湿疹用什么药膏最有效| 摔伤用什么药好得快| 姝是什么意思| 水土不服吃什么药管用| 送百合花代表什么意思| 看见喜鹊有什么预兆| 为什么门牙突然有缝了| 胃糜烂吃什么药可以根治| epo是什么意思| 明年是什么年啊| 黄磊为什么不娶刘若英| 状元红又叫什么荔枝| 美国有什么特产| 垫脚石是什么意思| 煎牛排用什么油好| thr是什么氨基酸| 山楂和什么泡水喝降血压| 树欲静而风不止是什么意思| 甲虫吃什么食物| 面肌痉挛是什么原因引起的| 谷氨酰转移酶高是什么原因| 智商105是什么水平| 维他命是什么| 利湿是什么意思| 海螺姑娘是什么意思| 亮油什么时候涂| 眼底出血用什么眼药水| 更年期综合症吃什么药| 见地是什么意思| 吃什么补气养血最快| 湿疹为什么要查肝功能| 胸闷是什么原因引起的| cn是什么意思啊| 红颜薄命的意思是什么| 背痛挂什么科| 不思量 自难忘什么意思| 摸摸头是什么意思| 高血糖可以吃什么| d二聚体是查什么的| 病理检查是什么意思| 膀胱炎做什么检查能看出来| 性是什么意思| 看腋臭挂什么科| 什么是坏血病| 上什么环最好最安全伤害小| 感冒了挂什么科| 氟利昂什么味道| 3.17是什么星座| cto是什么意思| 八方来财是什么意思| 不伤肝的他汀类药是什么| 花是植物的什么器官| 硬卧是什么样子的| 掷是什么意思| 11月14日什么星座| 麝香是什么动物| 淡盐水是什么水| ca724偏高是什么意思| 总蛋白偏高有什么危害| 梦见棺材是什么意思| 儿童结膜炎用什么眼药水| 玉屏风颗粒主治什么| 抗战纪念日为什么是9月3日| 什么生肖最旺鸡| 空调抽湿是什么意思| 女人亏气亏血吃什么补的快| 长闭口是什么原因造成的| 健脾胃吃什么食物好| 脚趾起水泡是什么原因| 孩子专注力差去什么医院检查| 乳腺结节挂什么科| 贡眉是什么茶| 白果是什么东西| 一什么茶| 病毒感染咳嗽吃什么药| 什么是自由度| 12点是什么时辰| 百度

2.杨洁导演的艺术人生,永远定格在了86版...

目录

一、LockSupport

1.1、LockSupport函数列表

1.2、基本使用

先 park 再 unpark

先 unpark 再 park

1.3、特点

与 Object 的 wait & notify 相比

二、LockSupport park & unpark原理

2.1、情况一,先调用park,再调用unpark

park 操作

unpark 操作

2.2、情况二,先调用unpark,再调用park

三、LockSupport Java源码解析

3.1 变量说明

变量是如何获取其实例对象的?

3.2 构造方法

3.3 两个特殊的方法

3.4 常用方法

1、unpark(Thread thread)方法

2、park(Object blocker)方法 和?park()方法

park(Object blocker)函数中要调用两次setBlocker函数

3、parkNanos(Object blocker, long nanos)方法 和?parkNanos(long nanos)方法

4、parkUntil(Object blocker, long deadline)方法 和?parkUntil(long deadline)方法

总结:


一、LockSupport

LockSupport是JDK中比较底层的类,用来创建锁和其他同步工具类的基本线程阻塞原语。
Java锁和同步器框架的核心AQS:AbstractQueuedSynchronizer,就是通过调用LockSupport.park()LockSupport.unpark()实现线程的阻塞和唤醒的。LockSupport很类似于二元信号量(只有1个许可证可供使用),如果这个许可还没有被占用,当前线程获取许可并继续执行;如果许可已经被占用,当前线程阻塞,等待获取许可。
LockSupport中的park()?和?unpark()?的作用分别是阻塞线程和解除阻塞线程,而且park()unpark()不会遇到“Thread.suspend 和 Thread.resume所可能引发的死锁”问题。因为park()?和?unpark()有许可的存在;调用?park()?的线程和另一个试图将其?unpark()?的线程之间的竞争将保持活性。?

1.1、LockSupport函数列表

public class LockSupport {
    // 返回提供给最近一次尚未解除阻塞的 park 方法调用的 blocker 对象,如果该调用不受阻塞,则返回 null。
    static Object getBlocker(Thread t);
    // 为了线程调度,禁用当前线程,除非许可可用。
    static void park();
    // 为了线程调度,在许可可用之前禁用当前线程。
    static void park(Object blocker);
    // 为了线程调度禁用当前线程,最多等待指定的等待时间,除非许可可用。
    static void parkNanos(long nanos);
    // 为了线程调度,在许可可用前禁用当前线程,并最多等待指定的等待时间。
    static void parkNanos(Object blocker, long nanos);
    // 为了线程调度,在指定的时限前禁用当前线程,除非许可可用。
    static void parkUntil(long deadline);
    // 为了线程调度,在指定的时限前禁用当前线程,除非许可可用。
    static void parkUntil(Object blocker, long deadline);
    // 如果给定线程的许可尚不可用,则使其可用。
    static void unpark(Thread thread);
}

说明:LockSupport是通过调用Unsafe函数中的接口实现阻塞和解除阻塞的。?

1.2、基本使用

// 暂停当前线程
LockSupport.park();
// 恢复某个线程的运行
LockSupport.unpark(暂停线程对象)

先 park 再 unpark
Thread t1 = new Thread(() -> {
    log.debug("start...");
    sleep(1);
    log.debug("park...");
    LockSupport.park();
    log.debug("resume...");
},"t1");
t1.start();
sleep(2);
log.debug("unpark...");
LockSupport.unpark(t1);

输出:

18:42:52.585 c.TestParkUnpark [t1] - start...
18:42:53.589 c.TestParkUnpark [t1] - park...
18:42:54.583 c.TestParkUnpark [main] - unpark...
18:42:54.583 c.TestParkUnpark [t1] - resume...

先 unpark 再 park
Thread t1 = new Thread(() -> {
    log.debug("start...");
    sleep(2);
    log.debug("park...");
    LockSupport.park();
    log.debug("resume...");
}, "t1");
t1.start();
sleep(1);
log.debug("unpark...");
LockSupport.unpark(t1);

输出:

18:43:50.765 c.TestParkUnpark [t1] - start...
18:43:51.764 c.TestParkUnpark [main] - unpark...
18:43:52.769 c.TestParkUnpark [t1] - park...
18:43:52.769 c.TestParkUnpark [t1] - resume...

1.3、特点

在调用对象的Wait之前当前线程必须先获得该对象的监视器(Synchronized),被唤醒之后需要重新获取到监视器才能继续执行。而LockSupport并不需要获取对象的监视器。?

与 Object 的 wait & notify 相比
  • 1、wait,notify 和 notifyAll 必须配合 Object Monitor 一起使用,而 park,unpark 不必。
  • 2、park & unpark 是以线程为单位来【阻塞】和【唤醒】线程,而 notify 只能随机唤醒一个等待线程,notifyAll是唤醒所有等待线程,但不那么【精确】。
  • 3、park & unpark 可以先 unpark,而 wait & notify 不能先 notify。

因为它们本身的实现机制不一样,所以它们之间没有交集,也就是说LockSupport阻塞的线程,notify/notifyAll没法唤醒。
虽然两者用法不同,但是有一点, LockSupport 的park和Object的wait一样也能响应中断。

public class LockSupportTest {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            LockSupport.park();
            System.out.println("thread:"+Thread.currentThread().getName()+"awake");
            },"t1");
        t.start();
        Thread.sleep(2000);
        //中断
        t.interrupt();
    }
}

二、LockSupport park & unpark原理

每个线程都会关联一个 Parker 对象,每个 Parker 对象都各自维护了三个角色:_counter(计数器)、?_mutex(互斥量)、_cond(条件变量)。?

2.1、情况一,先调用park,再调用unpark

park 操作

  1. 当前线程调用?Unsafe.park()?方法
  2. 检查?_counter?,本情况为 0,这时,获得?_mutex?互斥锁
  3. 线程进入?_cond?条件变量阻塞
  4. 设置?_counter = 0?

    unpark 操作
  5. 调用?Unsafe.unpark(Thread_0)?方法,设置?_counter?为 1

  6. 唤醒?_cond?条件变量中的?Thread_0
  7. Thread_0?恢复运行
  8. 设置?_counter?为 0?

    2.2、情况二,先调用unpark,再调用park
  9. 调用?Unsafe.unpark(Thread_0)?方法,设置?_counter?为 1

  10. 当前线程调用?Unsafe.park()?方法
  11. 检查?_counter?,本情况为 1,这时线程无需阻塞,继续运行
  12. 设置?_counter?为 0?

    三、LockSupport Java源码解析

    3.1 变量说明

    public class LockSupport {
     // Hotspot implementation via intrinsics API
     //unsafe常量,设置为使用Unsafe.compareAndSwapInt进行更新
     //UNSAFE字段表示sun.misc.Unsafe类,一般程序中不允许直接调用
     private static final sun.misc.Unsafe UNSAFE;
     //表示parkBlocker在内存地址的偏移量
     private static final long parkBlockerOffset;
     //表示threadLocalRandomSeed在内存地址的偏移量,此变量的作用暂时还不了解
     private static final long SEED;
     //表示threadLocalRandomProbe在内存地址的偏移量,此变量的作用暂时还不了解
     private static final long PROBE;
     //表示threadLocalRandomSecondarySeed在内存地址的偏移量
     // 作用是 可以通过nextSecondarySeed()方法来获取随机数
     private static final long SECONDARY;
    }

    变量是如何获取其实例对象的?
    public class LockSupport {
     static {
         try {
             //实例化unsafe对象
             UNSAFE = sun.misc.Unsafe.getUnsafe();
             Class<?> tk = Thread.class;
             //利用unsafe对象来获取parkBlocker在内存地址的偏移量
             parkBlockerOffset = UNSAFE.objectFieldOffset(tk.getDeclaredField("parkBlocker"));
             //利用unsafe对象来获取threadLocalRandomSeed在内存地址的偏移量
             SEED = UNSAFE.objectFieldOffset(tk.getDeclaredField("threadLocalRandomSeed"));
             //利用unsafe对象来获取threadLocalRandomProbe在内存地址的偏移量  
             PROBE = UNSAFE.objectFieldOffset(tk.getDeclaredField("threadLocalRandomProbe"));
             //利用unsafe对象来获取threadLocalRandomSecondarySeed在内存地址的偏移量  
             SECONDARY = UNSAFE.objectFieldOffset(tk.getDeclaredField("threadLocalRandomSecondarySeed"));
         } catch (Exception ex) { throw new Error(ex); }
     }
    }

    由上面代码可知这些变量是通过static代码块在类加载的时候就通过unsafe对象获取其在内存地址的偏移量了。?

    3.2 构造方法

    public class LockSupport {
     //LockSupport只有一个私有构造函数,无法被实例化。
     private LockSupport() {} // Cannot be instantiated.
    }

    3.3 两个特殊的方法

    public class LockSupport {
     //设置线程t的parkBlocker字段的值为arg
     private static void setBlocker(Thread t, Object arg) {
         // Even though volatile, hotspot doesn't need a write barrier here.
         //尽管hotspot易变,但在这里并不需要写屏障。
         UNSAFE.putObject(t, parkBlockerOffset, arg);
     }
     //获取当前线程的Blocker值
     public static Object getBlocker(Thread t) {
         //若当前线程为空就抛出异常
         if (t == null)
             throw new NullPointerException();
         //利用unsafe对象获取当前线程的Blocker值 
         return UNSAFE.getObjectVolatile(t, parkBlockerOffset);
     }
    }

    1、unpark(Thread thread)方法
    public class LockSupport {
     //释放该线程的阻塞状态,即类似释放锁,只不过这里是将许可设置为1
     public static void unpark(Thread thread) {
         //判断线程是否为空
         if (thread != null)
             //释放该线程许可
             UNSAFE.unpark(thread);
     }
    }

    2、park(Object blocker)方法 和?park()方法
    public class LockSupport {
     //阻塞当前线程,并且将当前线程的parkBlocker字段设置为blocker
     public static void park(Object blocker) {
         //获取当前线程
         Thread t = Thread.currentThread();
         //将当前线程的parkBlocker字段设置为blocker
         setBlocker(t, blocker);
         //阻塞当前线程,第一个参数表示isAbsolute,是否为绝对时间,第二个参数就是代表时间
         UNSAFE.park(false, 0L);
         //重新可运行后再此设置Blocker
         setBlocker(t, null);
     }
     //无限阻塞线程,直到有其他线程调用unpark方法
     public static void park() {
         UNSAFE.park(false, 0L);
     }   
    }

    说明:

  • 调用park函数时,首先获取当前线程,然后设置当前线程的parkBlocker字段,即调用setBlocker函数, 之后调用Unsafe类的park函数,之后再调用setBlocker函数。?

    park(Object blocker)函数中要调用两次setBlocker函数
  • 1、调用park函数时,当前线程首先设置好parkBlocker字段,然后再调用?Unsafe的park函数,此时,当前线程就已经阻塞了,等待该线程的unpark函数被调用,所以后面的一个?setBlocker函数无法运行,unpark函数被调用,该线程获得许可后,就可以继续运行了,也就运行第二个?setBlocker,把该线程的parkBlocker字段设置为null,这样就完成了整个park函数的逻辑。

  • 2、如果没有第二个?setBlocker,那么之后没有调用park(Object blocker),而直接调用getBlocker函数,得到的还是前一个?park(Object blocker)设置的blocker,显然是不符合逻辑的。总之,必须要保证在park(Object blocker)整个函数 执行完后,该线程的parkBlocker字段又恢复为null。

所以,park(Object)型函数里必须要调用setBlocker函数两次。?

3、parkNanos(Object blocker, long nanos)方法 和?parkNanos(long nanos)方法
public class LockSupport {
    //阻塞当前线程nanos秒
    public static void parkNanos(Object blocker, long nanos) {
        //先判断nanos是否大于0,小于等于0都代表无限等待
        if (nanos > 0) {
            //获取当前线程
            Thread t = Thread.currentThread();
            //将当前线程的parkBlocker字段设置为blocker
            setBlocker(t, blocker);
            //阻塞当前线程现对时间的nanos秒
            UNSAFE.park(false, nanos);
            //将当前线程的parkBlocker字段设置为null
            setBlocker(t, null);
        }
    }   
    //阻塞当前线程nanos秒,现对时间
    public static void parkNanos(long nanos) {
        if (nanos > 0)
            UNSAFE.park(false, nanos);
    }   
}

4、parkUntil(Object blocker, long deadline)方法 和?parkUntil(long deadline)方法
public class LockSupport {
    //将当前线程阻塞绝对时间的deadline秒,并且将当前线程的parkBlockerOffset设置为blocker
    public static void parkUntil(Object blocker, long deadline) {
        //获取当前线程
        Thread t = Thread.currentThread();
        //设置当前线程parkBlocker字段设置为blocker
        setBlocker(t, blocker);
        //阻塞当前线程绝对时间的deadline秒
        UNSAFE.park(true, deadline);
        //当前线程parkBlocker字段设置为null
        setBlocker(t, null);
    }
    //将当前线程阻塞绝对时间的deadline秒
    public static void parkUntil(long deadline) {
        UNSAFE.park(true, deadline);
    }   
}

总结:

LockSupport 和 CAS 是Java并发包中很多并发工具控制机制的基础,它们底层其实都是依赖Unsafe实现。很多锁的类都是基于LockSupport的park和unpark来实现的,所以了解LockSupport类是非常重要的。

如果小假的内容对你有帮助,请点赞评论收藏。创作不易,大家的支持就是我坚持下去的动力!

评论 75
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员小假

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值
贤良淑德后半句是什么 什么面条好吃 人生苦短什么意思 胃强脾弱吃什么中成药 印度神油是什么东西
af是什么 老虎下山下一句是什么 血糖偏低是什么原因引起的 耳道湿疹用什么药 鞭炮笋学名叫什么
高密度脂蛋白胆固醇高是什么意思 碳13是检查什么的 什么叫西米 nuxe是什么牌子护肤品 全棉和纯棉有什么区别
娥皇女英是什么意思 经常拉肚子是什么原因引起的 前列腺不能吃什么食物 杰士邦是什么 胸口长痘是什么原因
时柱金舆是什么意思hcv9jop3ns6r.cn 卵巢保养吃什么好hcv8jop2ns4r.cn 气郁症是什么症状hcv8jop9ns3r.cn 什么深似海hcv8jop9ns3r.cn 锦纶是什么bjhyzcsm.com
咳嗽咳白痰是什么症状gangsutong.com 脑白质变性是什么病hcv9jop1ns3r.cn 脸部爱出油是什么原因hcv9jop4ns4r.cn 5月有什么节日hcv8jop3ns3r.cn 褪黑素有什么用hcv8jop8ns5r.cn
妈妈的弟弟的老婆叫什么hcv9jop2ns5r.cn 海狗是什么动物hcv9jop1ns5r.cn 64年属什么的hcv9jop5ns3r.cn 荷字五行属什么hcv8jop8ns4r.cn 薄荷不能和什么一起吃hcv9jop0ns8r.cn
农历12月18日是什么星座hcv8jop7ns1r.cn 改姓需要什么手续hcv8jop2ns8r.cn coscia是什么品牌hcv7jop6ns0r.cn 鹰的天敌是什么动物hcv7jop4ns6r.cn 白色泡沫痰是什么原因hcv8jop7ns8r.cn
百度