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调用而唤醒,是因为更底层的原因。

  • 相关阅读:
    二分图最大匹配的König定理及其证明
    HDOJ 2389 Rain on your Parade
    HDOJ 1083 Courses
    HDOJ 2063 过山车
    POJ 1469 COURSES
    UESTC 1817 Complete Building the Houses
    POJ 3464 ACM Computer Factory
    POJ 1459 Power Network
    HDOJ 1532 Drainage Ditches
    HDU 1017 A Mathematical Curiosity
  • 原文地址:https://www.cnblogs.com/mr-ziyoung/p/13388315.html
Copyright © 2011-2022 走看看