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

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

创新互联长期为超过千家客户提供的网站建设服务,团队从业经验10年,关注不同地域、不同群体,并针对不同对象提供差异化的产品和服务;打造开放共赢平台,与合作伙伴共同营造健康的互联网生态环境。为安定企业提供专业的网站建设、网站制作,安定网站改版等技术服务。拥有10余年丰富建站经验和众多成功案例,为您定制开发。

一、传统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唤醒这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是创新互联,小编将为大家推送更多相关知识点的文章,欢迎关注!


分享文章:如何理解LockSupport类中的park等待和unpark唤醒
网页地址:http://ybzwz.com/article/ipdidi.html