这篇文章主要讲解了“如何理解LockSupport类中的park等待和unpark唤醒”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“如何理解LockSupport类中的park等待和unpark唤醒”吧!
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()方法不能脱离同步代码块(锁)单独使用
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线程将不会被唤醒
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唤醒这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是亿速云,小编将为大家推送更多相关知识点的文章,欢迎关注!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。