文档章节

Java多线程学习(七)

kakayang2011
 kakayang2011
发布于 2016/03/07 15:16
字数 1635
阅读 44
收藏 3

等待/通知模式中最经典的案例当属生产者/消费者模式,此模式有几种变形,但都是基于wait/notify的。

生产者/消费者模式有两种类型:操作值的和操作栈的。下面分别从这两方面来讲解生产者/消费者模式。

  1. 操作值

    ①一生产与一消费

    public class P {
       private String lock;  //操作值
      public P(String lock){
           this.lock = lock;
      }
       public void setValue(){
            synchronized(lock){
               if(!ValueObject.value.equals("")){
                    lock.wait();
               }
               String value = System.currentTimeMillis();
               ValueObject.value = value;
               lock.notify();
           }
       }
    }
    public class C {
       private String lock;  //操作值
       public C(String lock){
           this.lock = lock;
       }
       public void getValue(){
            synchronized(lock){
               if(ValueObject.value.equals("")){
                    lock.wait();
               }
               ValueObject.value = "";
               lock.notify();
           }
       }
    }
    public class ValueObject {
       public static String value = "";
    }
    public class ThreadP extends Thread {
       private P p;
       public ThreadP(P p){
           this.p = p;
       }
       public void run(){
          while(true){
             p.setValue();
         }
       }
    }
        
    public class ThreadC extends Thread {
       private C c;
       public ThreadC(C c){
           this.c = c;
       }
       public void run(){
          while(true){
             c.getValue();
         }
       }
    }  
    public class Run{
       public static void main(String [] args){
          String lock = new String("");
         P p = new P(lock);
         C c = new C(lock);
         ThreadP tp = new ThreadP(p);
         ThreadC tc = new ThreadC(c);
         tp.start();
         tc.start();
      }
    }
    运行结果:不断地循环运行生产者的setValue方法和消费者的getValue方法,不停的交换数据。

    分析:tp线程首先运行,lock == “”,生产者p发现尚未生产,所以先生产,在下一次循环中发现value != “”;即生产出的产品尚未被消费,所以先等待进入wait状态。tc线程开始运行,发现 value!=“”,产品还没有被消费,所以先消费,在下一次循环中发现 value == “”,即生产者还没有生产,所以进入wait状态,让出控制权,让生产者继续生产...进入一种无限循环中。

    ②多生产与多消费:操作值--有可能进入假死状态

    public class P {
       private String lock;  //操作值
      public P(String lock){
           this.lock = lock;
      }
       public void setValue(){
            synchronized(lock){
               while(!ValueObject.value.equals("")){
                    lock.wait();
               }
               String value = System.currentTimeMillis();
               ValueObject.value = value;
               lock.notify();
           }
       }
    }
    public class C {
       private String lock;  //操作值
       public C(String lock){
           this.lock = lock;
       }
       public void getValue(){
            synchronized(lock){
               while(ValueObject.value.equals("")){
                    lock.wait();
               }
               ValueObject.value = "";
               lock.notify();
           }
       }
    }
    public class ValueObject {
       public static String value = "";
    }
    public class ThreadP extends Thread {
       private P p;
       public ThreadP(P p){
           this.p = p;
       }
       public void run(){
          while(true){
             p.setValue();
         }
       }
    }
        
    public class ThreadC extends Thread {
       private C c;
       public ThreadC(C c){
           this.c = c;
       }
       public void run(){
          while(true){
             c.getValue();
         }
       }
    }  
    public class Run{
       public static void main(String [] args){
          String lock = new String("");
         P p = new P(lock);
         C c = new C(lock);
         ThreadP tp1 = new ThreadP(p);
         ThreadP tp2 = new ThreadP(p);
         ThreadC tc1 = new ThreadC(c);
         ThreadC tc1 = new ThreadC(c);
         tp1.start();
         tc1.start();
         tp2.start();
         tc2.start();
       }
     }  
     运行结果:极有可能出现假死情况,即四个线程都处于wait状态。

    分析:首先生产者1在检查了条件,即value==“”后开始生产,然后调用notify方法,在下一次while循环检查条件是发现value != “”;进入wait状态;下一次若被生产者2抢到控制权,发现已经生产,则生产者2直接进入wait状态。然后消费者1检查发现value != “”,开始消费,在下一次while循环中,发现value == “”,即尚未生产,则进入wait状态,这时若消费者2抢到了锁, 也发现尚未生产,则也进入wait状态;生产者1又一次抢到了锁,开始生产,在下一次while循环中,发现尚未消费,进入了wait状态;此时若又由生产者2拿到了锁,它发现尚未消费,则也进入wait状态。现在,四个进程都在等待,进入了假死状态。

    “假死”状态是因为线程连续唤醒同类造成的,生产者1唤醒生产者2,消费者1唤醒消费者2。如何解决?不仅唤醒同类,而且将异类也一起唤醒。即将生产者和消费者中的notify方法换为notifyAll方法。

     

  2. 操作栈

    ①一生产与一消费

    public class MyStack {
           private List list = new ArrayList();     //操作栈
  3.        synchronized public void push(){
                   if(list.size() == 1){
                           this.wait();
                   }
                   list.add("str");
                   this.notify();
            }
            synchronized public void pop(){
                   if(list.size() == 0){
                          this.wait();
                   }
                   list.remove(0);
                   this.notify();
             }
    }
    public class P {
       private MyStack myStack;
       public P(MyStack s){
           this.myStack = s;
       }
       public void pushService(){
            myStack.push();
       }
    }
    public class C {
       private MyStack myStack;
       public C(MyStack s){
           this.myStack = s;
       }
       public void popService(){
            myStack.pop();
       }
    }
    public class ThreadP extends Thread {
       private P p;
       public ThreadP(P p){
           this.p = p;
       }
       public void run(){
          while(true){
             p.pushService();
         }
       }
    }
        
    public class ThreadC extends Thread {
       private C c;
       public ThreadC(C c){
           this.c = c;
       }
       public void run(){
          while(true){
             c.popService();
         }
       }
    }  
    public class Run{
       public static void main(String [] args){
          MyStack muStack= new MyStack();
          P p = new P(myStack);
          C c = new C(myStack);
          ThreadP tp = new ThreadP(p);
          ThreadC tc = new ThreadC(c);
          tp.start();
          tc.start();
       }
     }  
     运行结果:size不会超过1,生产和消费过程在轮流执行。

    分析:生产者首先运行,size==0,首先生产,在下一次while循环是进入wait状态;消费者运行,首先消费,在下一次while循环时发现还未生产,进入wait状态。唤醒生产者进行生产。。。不断循环此过程。

    ②一生产和多消费---可能遭遇条件改变抛出的异常和假死情况

    上例的代码不进行更改,只修改运行类如下:
    public class Run{
       public static void main(String [] args){
          MyStack muStack= new MyStack();
          P p = new P(myStack);
          C c1 = new C(myStack);
          C c2 = new C(myStack);
          C c3 = new C(myStack);
          C c4 = new C(myStack);
          C c5 = new C(myStack);
          ThreadP tp = new ThreadP(p);
          ThreadC tc1 = new ThreadC(c1);
          ThreadC tc2 = new ThreadC(c2);
          ThreadC tc3 = new ThreadC(c3);
          ThreadC tc4 = new ThreadC(c4);
          ThreadC tc5 = new ThreadC(c5);
          tp.start();
          tc.start();
       }
    }  
    运行结果:在某些几率下出现异常IndexOutOfBoundException。

    分析:p1进行生产后进入wait状态;c1开始消费,消费后进入wait状态,假设此时消费者c2、c3、c4、c5都进入wait状态。p1有开始生产,然后唤醒c2进行消费后又进入wait状态,然后唤醒c3,由于代码中是if结构,从wait中醒来时不会再检查条件,而是直接进行remove操作,这时抛出异常。

    这是因为条件改变后没有得到即时的响应,解决这个问题的方法是,将if改为while语句即可;

     public class MyStack {
           private List list = new ArrayList();     //操作栈
           synchronized public void push(){
                   while(list.size() == 1){
                           this.wait();
                   }
                   list.add("str");
                   this.notify();
            }
            synchronized public void pop(){
                   while(list.size() == 0){
                          this.wait();
                   }
                   list.remove(0);
                   this.notify();
             }
    }
    运行结果:这次不会抛出异常,但是出现假死情况。

    分析:因为是while循环,那么消费者在检查条件后,优惠进入wait状态,这是生产者和所有的消费者都进入了wait状态。

    解决方法:将notify()方法改为notifyAll()方法即可。

      public class MyStack {
           private List list = new ArrayList();     //操作栈
  4.        synchronized public void push(){
                   while(list.size() == 1){
                           this.wait();
                   }
                   list.add("str");
                   this.notifyAll();
            }
            synchronized public void pop(){
                   while(list.size() == 0){
                          this.wait();
                   }
                   list.remove(0);
                   this.notifyAll();
             }
    }
    运行结果:这次不会抛出异常,页不会出现假死情况。程序会正常运行下去。
  5. 注明:最后一种while + notifyAll的类型同样适用于多生产者和一消费者,以及多生产者和多消费者这两种情况。

© 著作权归作者所有

kakayang2011
粉丝 8
博文 22
码字总数 18886
作品 0
程序员
私信 提问
Java多线程学习(五)线程间通信知识点补充

系列文章传送门: Java多线程学习(一)Java多线程入门 Java多线程学习(二)synchronized关键字(1) java多线程学习(二)synchronized关键字(2) Java多线程学习(三)volatile关键字 Ja...

一只蜗牛呀
2018/04/16
0
0
Java多线程学习(二)synchronized关键字(2)

系列文章传送门: Java多线程学习(一)Java多线程入门 Java多线程学习(二)synchronized关键字(1) java多线程学习(二)synchronized关键字(2) Java多线程学习(三)volatile关键字 Ja...

一只蜗牛呀
2018/04/16
0
0
Java多线程学习(四)等待/通知(wait/notify)机制

系列文章传送门: Java多线程学习(一)Java多线程入门 Java多线程学习(二)synchronized关键字(1) java多线程学习(二)synchronized关键字(2) Java多线程学习(三)volatile关键字 Ja...

一只蜗牛呀
2018/04/16
0
0
JAVA基础再回首(三十)——JAVA基础再回首完美结束,感概万千!

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/m366917/article/details/52724939 JAVA基础再回首(三十)——JAVA基础再回首完美结束,感概万千! 经过了几...

Aduroidpc
2016/10/02
0
0
一份关于 Java、Kotlin 与 Android 的学习笔记

JavaKotlinAndroidLearn 这是一份关于 Java 、Kotlin 、Android 的学习笔记,既包含对基础知识点的介绍,也包含对一些重要知识点的源码解析,笔记的大纲如下所示: Java 重拾Java(0)-基础知...

叶应是叶
2018/08/08
0
0

没有更多内容

加载失败,请刷新页面

加载更多

代码规范

代码格式化 安装vscode插件:Prettier - Code formatter 格式化配置:将下列配置写入到vscode的settings.json文件 (遵照代码格式化) "prettier.disableLanguages": ["vue"], "prettier.......

TreeZhou0511
36分钟前
1
0
python实现人工神经网络的一个例子

人工神经网络已经有无数的开源框架,比如tensorflow,caffe等,可以直接用。但最近需要做一个小样例,把基本思想讲一讲,因此自己写了一个demo,以供参考。 下面直接上代码,代码中有注释,比...

propagator
39分钟前
3
0
远程dubugger

1、在tomcat的bin下/data/project/XXX/apache-tomcat-8.5.23/bin 在catalina.bat文件中新增如下即可 JAVA_OPTS="-Xmx1024m -Xms1024m -agentlib:jdwp=transport=dt_socket,server=y,suspend......

一只小青蛙
57分钟前
1
0
jemter 连接MySQL

jemter 连接MySQL 点击测试计划,测试计划最后”添加目录或jar包到ClassPath“,点击浏览,添加mysql-connector.jar mysql-connector.jar的下载地址: https://mvnrepository.com/artifact/my...

xiaobai1315
今天
5
0
第一次尝试用python实现zabbix主机的批量添加批量删除及模板导入脚本

Python3入门练手尝试篇 API参考: zabbix API 4.0版本:https://www.zabbix.com/documentation/4.0/zh/manual/api #!/bin/python3import xlrd,os,json,requests,sys#参考zabbix API 4.0版本......

平头哥-Enjoystudy
今天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部