zoukankan      html  css  js  c++  java
  • java多线程回顾4:线程通信

    1、线程的协调运行

    线程的协调运行有一个经典案例,即生产者和消费者问题。

    假设有一个货架,生产者往货架上放货物,消费者从货架上取货物。

    为了方便讲解,制定一个规则,生产者每放上一个货物,消费者就得取走一个货物。不允许连续放两次,也不允许连续取两次。

    为了实现这个功能,可以使用wait()notify()notifyAll()三个方法。注意,这三个方法不属于Thread类,而是属于Object类,且必须由同步监视器对象调用。详细用法如下:

    <![if !supportLists]>Ø  <![endif]>对于使用synchronized修饰的同步方法,因为默认实例(this)就是同步监视器对象,所以可以在同步方法中直接调用这三个方法。

    <![if !supportLists]>Ø  <![endif]>对于使用synchronized修饰的同步代码块,同步监视器对象是synchronized后括号里的对象,必须使用该对象调用这三个方法

    这三个方法的作用如下:

    <![if !supportLists]>Ø  <![endif]>wait():使当前线程等待,直到其他线程调用该同步监视器的notify()notifyAll()来唤醒该线程。调用wait()的当前线程会释放对该同步监视器的锁定。

    <![if !supportLists]>Ø  <![endif]>notify():唤醒在此同步监视器上的单个线程,如果有多个线程在等待,则随机唤醒一个。被唤醒的线程会在当前线程释放对同步监视器的锁定后开始运行。

    <![if !supportLists]>Ø  <![endif]>notifyAll():唤醒在此同步监视器上的所有线程,其余和notify()相同。

    货架代码如下:

    publicclass Shelf {

        //是否有货物的标志,true:有;false:没有

        privatebooleanflag;

       

        //放入货物的方法

        publicsynchronizedvoid product(){

           //flag为假时表示没有货物,此时可放入货物

           if(!flag){

               flag = true;

               System.out.println(Thread.currentThread().getName()+"放入了货物。");

               notifyAll();

           }else{

               try {

                  wait();

               } catch (InterruptedException e) {

                  e.printStackTrace();

               }

           }

        }

        //取走货物的方法

        publicsynchronizedvoid custom(){

           //flag为真时表示有货物,此时可取走货物

           if (flag) {

               flag = false;

               System.out.println(Thread.currentThread().getName()+"取走了货物。");

               notifyAll();

           }else{

               try {

                  wait();

               } catch (InterruptedException e) {

                  e.printStackTrace();

               }

           }

        }

    }

    生产者线程代码如下:

    publicclass ProducerThread implements Runnable{

        private Shelf shelf;

        public ProducerThread(Shelf shelf){

           this.shelf = shelf;

        }

        @Override

        publicvoid run() {

           //放十次货物

           for (int i = 0; i < 10; i++) {

               shelf.product();

           }

        }

    }

    消费者线程代码如下:

    publicclass CustomerThread implements Runnable{

        private Shelf shelf;

        public CustomerThread(Shelf shelf){

           this.shelf = shelf;

        }

        @Override

        publicvoid run() {

           //取十次货物

           for (int i = 0; i < 10; i++) {

               shelf.custom();

           }

        }

    }

    测试代码如下:

    publicclass TestShelf {

        publicstaticvoid main(String[] args) {

           Shelf shelf = new Shelf();

           ProducerThread producerThread = new ProducerThread(shelf);

           CustomerThread customerThread = new CustomerThread(shelf);

          

           new Thread(producerThread,"生产者1").start();

           new Thread(producerThread,"生产者2").start();

           new Thread(customerThread,"消费者").start();

        }

    }

    运行结果如下:

    生产者1放入了货物。

    消费者取走了货物。

    生产者1放入了货物。

    消费者取走了货物。

    生产者2放入了货物。

    消费者取走了货物。

    生产者1放入了货物。

    消费者取走了货物。

    生产者1放入了货物。

    消费者取走了货物。

    生产者2放入了货物。

    消费者取走了货物。

    生产者2放入了货物。

    消费者取走了货物。

    生产者2放入了货物。

    上例中启动了两个生产者一个消费者,可见生产和消费是交替进行的,满足生产者每放上一个货物,消费者就得取走一个货物的规则。

    最后程序将阻塞,因为消费者比较少,生产者最后只能一直等消费者来取走货物。同时消费者取货物的次数也小于10次,因为有时取货时没货了。

    2、使用条件变量协调运行

    如果不使用synchronized,而是使用Lock对象来保证同步,则不能使用wait()notify()notifyAll()这三个方法。此时可使用Condition类来实现协作。

    Condition实例必须从Lock实例中获得,也有三个方法,如下:

    <![if !supportLists]>Ø  <![endif]>await():和wait()类似,使当前线程等待,直到被唤醒。相比wait()await()有更多的变体,这个以后再说。

    <![if !supportLists]>Ø  <![endif]>signal():和notify()类似,唤醒当前Lock对象上的单条线程。

    <![if !supportLists]>Ø  <![endif]>signalAll()notifyAll()类似,唤醒当前Lock对象上的所有线程。

    生产者线程、消费者线程、测试代码和测试结果的代码跟上一节完全一样,这里只放出货架的代码。

    货架的代码:

    publicclass Shelf {

        //是否有货物的标志,true:有;false:没有

        privatebooleanflag;

        //定义Lock对象

        privatefinal Lock lock = new ReentrantLock();

        //获取Lock对象对应的Condition对象

        privatefinal Condition condition= lock.newCondition();

       

        //放入货物的方法

        publicvoid product(){

           lock.lock();

           try {

               //flag为假时表示没有货物,此时可放入货物

               if(!flag){

                  flag = true;

                  System.out.println(Thread.currentThread().getName()+"放入了货物。");

                  condition.signalAll();

               }else{

                  condition.await();

               }

           } catch (Exception e) {

               e.printStackTrace();

           }finally{

               lock.unlock();

           }

          

          

        }

        //取走货物的方法

        publicsynchronizedvoid custom(){

           lock.lock();

           try {

               //flag为真时表示有货物,此时可取走货物

               if (flag) {

                  flag = false;

                  System.out.println(Thread.currentThread().getName()+"取走了货物。");

                  condition.signalAll();

               }else{

                  condition.await();

               }

           } catch (Exception e) {

               // TODO: handle exception

               e.printStackTrace();

           }finally{

               lock.unlock();

           }

        }

    }

    3、使用管道流通信

    线程之间使用管道流通信并不是最好的方式,通常推荐使用共享资源的方式交换信息。所以本节内容了解一下就好。

    管道流有三种形式,分别是管道字节流(PipedInputStreamPipedOutputStream)、管道字符流(PipedReaderPipedWriter)和新IO的管道ChannelPipe.SinkChannelPipe.SourceChannel)。

    使用步骤如下:

    <![if !supportLists]>Ø  <![endif]>创建输入管道输入流和管道输出流对象。

    <![if !supportLists]>Ø  <![endif]>使用管道输入流或管道输出流的connect方法把输入流和输出流连接起来。

    <![if !supportLists]>Ø  <![endif]>将管道输入流、管道输出流分别传入两个线程

    <![if !supportLists]>Ø  <![endif]>线程使用各自的管道流通信

    代码就不贴了

     

     





  • 相关阅读:
    Jmeter之http性能测试实战 非GUI模式压测 NON-GUI模式 结果解析TPS——干货(十一)
    UI Recorder 自动化测试 回归原理(九)
    UI Recorder 自动化测试 录制原理(八)
    UI Recorder 自动化测试 整体架构(七)
    UI Recorder 自动化测试 配置项(六)
    UI Recorder 自动化测试 工具栏使用(五)
    UI Recorder 自动化测试 回归测试(四)
    UI Recorder 自动化测试 录制(三)
    UI Recorder 自动化测试工具安装问题疑难杂症解决(二)
    UI Recorder 自动化测试安装教程(一)
  • 原文地址:https://www.cnblogs.com/bailiyi/p/5310639.html
Copyright © 2011-2022 走看看