千家信息网

如何理解LockSupport类中的park等待和unpark唤醒

发表于:2025-01-22 作者:千家信息网编辑
千家信息网最后更新 2025年01月22日,这篇文章主要讲解了"如何理解LockSupport类中的park等待和unpark唤醒",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"如何理解LockS
千家信息网最后更新 2025年01月22日如何理解LockSupport类中的park等待和unpark唤醒

这篇文章主要讲解了"如何理解LockSupport类中的park等待和unpark唤醒",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"如何理解LockSupport类中的park等待和unpark唤醒"吧!

一、传统synchronized隐式锁

package com.lau.javabase.lock.LockSupport;import java.util.concurrent.TimeUnit;/** * 使用LockSupport之前,synchronized传统方式存在的问题: * 1、wait()和notify()方法不能脱离同步代码块(锁)单独使用 * 2、B线程的notify()方法在A线程的wait()之前执行的话,A线程将不会被唤醒 */public class BeforeUseTraditional {    public static void main(String[] args) {        Object lockObj = new Object();        //线程A        new Thread(() -> {//            try {//                TimeUnit.SECONDS.sleep(3);//            } catch (InterruptedException e) {//                e.printStackTrace();//            }//            synchronized (lockObj){                System.out.println(Thread.currentThread().getName() + " come in...");                try {                    lockObj.wait();                } catch (InterruptedException e) {                    e.printStackTrace();                }                System.out.println(Thread.currentThread().getName() + " awakened...");//            }        }, "A").start();//        try {//            TimeUnit.SECONDS.sleep(3);//        } catch (InterruptedException e) {//            e.printStackTrace();//        }        //线程B唤醒线程A        new Thread(() -> {//            synchronized (lockObj){                lockObj.notify();                System.out.println(Thread.currentThread().getName() + " notify...");//            }        }, "B").start();    }}

输出:

A come in...Exception in thread "A" Exception in thread "B" java.lang.IllegalMonitorStateException        at java.lang.Object.notify(Native Method)        at com.lau.javabase.lock.LockSupport.BeforeUseTraditional.lambda$main$1(BeforeUseTraditional.java:42)        at java.lang.Thread.run(Thread.java:745)java.lang.IllegalMonitorStateException        at java.lang.Object.wait(Native Method)        at java.lang.Object.wait(Object.java:502)        at com.lau.javabase.lock.LockSupport.BeforeUseTraditional.lambda$main$0(BeforeUseTraditional.java:25)        at java.lang.Thread.run(Thread.java:745)Process finished with exit code 0

结论:wait()和notify()方法不能脱离同步代码块(锁)单独使用

二、ReentrantLock显示锁

package com.lau.javabase.lock.LockSupport;import java.util.concurrent.TimeUnit;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;/** * 使用LockSupport之前,Lock方式存在的问题: * 1、await()和signal()方法不能脱离同步代码块(锁)单独使用 * 2、B线程的和signal()方法在A线程的await()之前执行的话,A线程将不会被唤醒 */public class BeforeUseLock {    public static void main(String[] args) {        Lock lock = new ReentrantLock();        Condition condition = lock.newCondition();        //线程A        new Thread(() -> {            try {                TimeUnit.SECONDS.sleep(3);            } catch (InterruptedException e) {                e.printStackTrace();            }               try {                    lock.lock();                   System.out.println(Thread.currentThread().getName() + " come in...");                   try {                       condition.await();                   } catch (Exception e) {                       e.printStackTrace();                   }                   System.out.println(Thread.currentThread().getName() + " awakened...");               }               finally {                   lock.unlock();               }        }, "A").start();//        try {//            TimeUnit.SECONDS.sleep(3);//        } catch (InterruptedException e) {//            e.printStackTrace();//        }        //线程B唤醒线程A        new Thread(() -> {            try{                lock.lock();                condition.signal();                System.out.println(Thread.currentThread().getName() + " notify...");            }            finally {                lock.unlock();            }        }, "B").start();    }}

输出:

B notify...A come in...

结论:B线程的和signal()方法在A线程的await()之前执行的话,A线程将不会被唤醒

三、LockSupport类

package com.lau.javabase.lock.LockSupport;import java.util.concurrent.TimeUnit;import java.util.concurrent.locks.LockSupport;/** * 使用LockSupport,不会存在以下问题: * 1、await()和signal()方法不能脱离同步代码块(锁)单独使用 * 2、B线程的和signal()方法在A线程的await()之前执行的话,A线程将不会被唤醒 */public class LockSupportTest {    public static void main(String[] args) {        //线程A        Thread threadA = new Thread(() -> {            try {                TimeUnit.SECONDS.sleep(3);            } catch (InterruptedException e) {                e.printStackTrace();            }           System.out.println(Thread.currentThread().getName() + " come in...");           LockSupport.park();           System.out.println(Thread.currentThread().getName() + " awakened...");        }, "A");        threadA.start();//        try {//            TimeUnit.SECONDS.sleep(3);//        } catch (InterruptedException e) {//            e.printStackTrace();//        }        //线程B唤醒线程A        new Thread(() -> {            LockSupport.unpark(threadA);            System.out.println(Thread.currentThread().getName() + " notify...");        }, "B").start();    }}

输出:

B notify...A come in...A awakened...

结论:使用LockSupport,不会存在以上两个问题

四、说明

LockSupport是用来创建锁和其他同步类的基本线程阻塞原语
LockSupport是一个线程阻塞工具类,所有的方法都是静态方法,可以让线程在任意位置阻塞,阻塞之后也有对应的唤醒方法。归根
结底,LockSupport调用的Unsafe中的native代码。

LockSupport提供park()和unpark()方法实现阻塞线程和解除线程阻塞的过程
LockSupport和每个使用它的线程都有一个许可(permit)关联。permit相当于1,0的开关,默认是0,
调用一次unpark就加1变成1,
调用一次park会消费permit,也就是将1变成o,同时park立即返回。
如再次调用park会变成阻塞(因为permit为零了会阻塞在这里,一直到permit变为1),这时调用unpark会把permit置为1。
每个线程都有一个相关的permit, permit最多只有一个,重复调用unpark也不会积累凭证。

形象的理解
线程阻塞需要消耗凭证(permit),这个凭证最多只有1个。
当调用park方法时
*如果有凭证,则会直接消耗掉这个凭证然后正常退出;
*如果无凭证,就必须阻塞等待凭证可用;
而unpark则相反,它会增加一个凭证,但凭证最多只能有1个,累加无效。

五、扩展

1、为什么可以先唤醒线程后阻塞线程?
因为unpark获得了一个凭证,之后再调用park方法,就可以名正言顺的凭证消费,故不会阻塞。

2、为什么唤醒两次后阻塞两次,但最终结果还会阻塞线程?
因为凭证的数量最多为1,连续调用两次unpark和调用一次unpark效果一样,只会增加一个凭证;
而调用两次park却需要消费两个凭证,证不够,不能放行。

感谢各位的阅读,以上就是"如何理解LockSupport类中的park等待和unpark唤醒"的内容了,经过本文的学习后,相信大家对如何理解LockSupport类中的park等待和unpark唤醒这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是,小编将为大家推送更多相关知识点的文章,欢迎关注!

0