zoukankan      html  css  js  c++  java
  • Java并发编程---线程间的通信基础篇

    线程间的通信可以分为文件共享、网络共享、共享变量、JDK提供的线程协调API(suspend/resume、wait/notify、park/unpark),今天我们着重来讲一下JDK提供的线程协作的API。

    suspend/resume

    suspend/resume方式的线程间协作时容易产生死锁,所以已经被JDK所废弃使用。(典型的生产者消费者模型中,生产者在占用了lock1锁之后,进行了suspend挂起操作,而消费者此时需要拿到lock1锁之后才能生产包子,那么此时就会产生死锁的现象;另外suspend方法要早于resume执行,否则也会产生死锁)

    wait/notify

    wait/notify的操作需要在同步代码块里边进行调用,否则会抛出异常(IllegalMonitorStateException)。wait方法会使当前线程进入等待状态,并加入该锁对象的等待集合中,并放弃当前持有的对象锁。notify/notifyAll方法是用来唤醒一个或者所有在等待该对象锁的线程。注意:wait方法虽然会自动解锁,但有顺序要求,它也要早于notify方法,否则会一直等待下去。

    park/unpark

    这两个方法是LockSuport提供的方法。线程调用park则等待“许可”,调用unpark则为指定线程提供“许可”,也即unpark(Thread thread)方法是需要有一个指定的线程参数的。这里的“许可”类似于一个已经存在的令牌,谁拿到令牌,谁就可以进行运行。对于park/unpark来说,这两个方法的执行没有先后顺序。他们不需要在同步代码块执行,一旦两个线程中,有一个线程在拿到了lock1锁之后,进行了park()的调用,那么其他线程如果再想要拿到lock1进行unpark操作就会拿不到lock1,从而形成死锁。
    示例代码如下:

    public class ParkUnparkDemo {
        //包子铺对象
        volatile Object bozipu = null;
        //资源锁
        Object baozi_lock = new Object();
    
        public static void main(String[] args) throws Exception{
            System.out.println("主线程开始运行");
            new ParkUnparkDemo().parkUnparkTest();
            //new ParkUnparkDemo().parkUnparkExceptionTest();
            //new ParkUnparkDemo().moreParkTest();
    //        new ParkUnparkDemo().moreUnparkTest();
    
        }
    
        /**
         * 测试park/unpark方法的使用
         */
       public  void parkUnparkTest() throws Exception {
            //开启消费者线程1:等待包子铺有包子之后,进行消费买包子
           Thread consumer =new Thread(() -> {
               //如果包子铺没有开业,则等待包子铺开业的"许可"
               System.out.println("等待包子铺开张。。。");
               //这里使用while进行是否被唤醒,不要使用if,因为会有伪唤醒的状态
               while (bozipu == null){
                   LockSupport.park();
                   System.out.println("包子已经买到,可以回家了!");
               }
           });
           consumer.start();
    
           //等待3秒钟开始创建包子铺
           Thread.sleep(3000L);
           bozipu = new Object();
           //给线程consummer颁发许可
           LockSupport.unpark(consumer);
           System.out.println("已经通知消费者包子铺开张");
       }
    
        /**
         * park/unpark方法异常情况测试
         */
       public   void parkUnparkExceptionTest() throws Exception{
           //开启消费者线程1:等待包子铺有包子之后,进行消费买包子
           Thread consumer =new Thread(() -> {
               //如果包子铺没有开业,则等待包子铺开业的"许可"
               System.out.println("等待包子铺开张。。。");
               if(bozipu == null){
                   //拿到baozi_lock锁
                   synchronized (baozi_lock) {
                       LockSupport.park();
                       System.out.println("包子已经买到,可以回家了!");
                   }
               }
           });
           consumer.start();
    
           //等待3秒钟开始创建包子铺
           Thread.sleep(3000);
           bozipu = new Object();
           //此时因为baozi_lock锁已经被消费者占有,无法继续执行
           synchronized (baozi_lock){
               //给线程consummer颁发许可
               LockSupport.unpark(consumer);
               System.out.println("已经通知消费者包子铺开张");
           }
       }
    
        /**
         * 多次调用unpark方法,调用一次park方法,线程会继续运行
         */
       public void moreUnparkTest(){
           LockSupport.unpark(Thread.currentThread());
           LockSupport.unpark(Thread.currentThread());
           LockSupport.unpark(Thread.currentThread());
           System.out.println("调用了三次unpark");
           LockSupport.park(Thread.currentThread());
           System.out.println("调用了一次park");
       }
    
        /**
         * 多次调用park方法,调用一次unpark方法,线程会进入等待状态
         */
        public void moreParkTest(){
            LockSupport.park(Thread.currentThread());
            System.out.println("调用了一次park");
    
            LockSupport.park(Thread.currentThread());
            LockSupport.park(Thread.currentThread());
            System.out.println("又调用了两次park");
            LockSupport.unpark(Thread.currentThread());
            System.out.println("调用了一次unpark方法");
        }
    }
    
    

    注意:在判断线程是否进入等待状态的时候,官方建议使用while来进行循环判断,因为处于等待中的线程可能会收到错误警报伪唤醒,如果不在循环中检查等待条件,程序就会在没有满足条件的情况下退出。
    伪唤醒是指,不是因为notify、notifyAll、unpark等api调用而唤醒,是因为更底层的原因。

  • 相关阅读:
    测试学习使用
    Tomcat version 6.0 only supports J2EE 1.2, 1.3, 1.4, and Java EE 5 Web modules
    web框架之Spring-MVC环境搭建
    serialVersionUID
    在eclipse中部署发布web项目 和 更改eclipseweb项目发布的路径
    servlet HttpSession 监听器
    java web 中的转发和重定向
    Spring MVC 中的 forward 和 redirect
    各个部门英语标识
    添加滚动条,内容多时显示,内容少时隐藏
  • 原文地址:https://www.cnblogs.com/mr-ziyoung/p/13388315.html
Copyright © 2011-2022 走看看