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

发布时间:2021-10-09 16:10:48 作者:iii
来源:亿速云 阅读:137

这篇文章主要讲解了“如何理解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唤醒这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是亿速云,小编将为大家推送更多相关知识点的文章,欢迎关注!

推荐阅读:
  1. Java中等待唤醒机制的原理是什么
  2. java等待唤醒机制的示例分析

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

locksupport park unpark

上一篇:Hadoop集群怎么搭建及如何进行Python操作

下一篇:什么是python元类

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》