生产者消费者模式实现

生产者和消费者模式,主要就是基于等待/通信模式,在之前我们已经学习wait和notify,下面我们就来实现代码。

一对一的生产消费

public class Test226 {
    private static String value="";
    private static Object lock = new Object();
    public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {

            @Override
            public void run() {
                // TODO Auto-generated method stub
                while(true) {
                    setValue();
                }

            }
        });
        Thread t2 = new Thread(new Runnable() {

            @Override
            public void run() {
                // TODO Auto-generated method stub
                while(true) {
                    getValue();
                }
            }
        });
        t1.start();
        t2.start();
    }

    public  static void setValue() {
        synchronized (lock) {
            if(!value.equals("")) {//你有货,我就等等
                try {
                    lock.wait();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            value = System.currentTimeMillis()+"------"+System.nanoTime();
            System.out.println("set值"+value);
            lock.notifyAll();//我生产好了你那去用
        }
    }

    public static void getValue() {
        synchronized (lock) {
            if(value.equals("")) {//库房没货,你倒是给我呀,我就等
                try {
                    lock.wait();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }

            //库房有货,我就拿

            System.out.println("get值"+System.currentTimeMillis()+"------"+System.nanoTime());
            value="";
            //你通知可以生产了。
            lock.notifyAll();
        }
    }
}

多生产多消费模式

之前单个线程生产,你生一个我拿一个,我们之间通信即可,但是在多个线程生产消费的时候,就出现问题。

package com.example.test;

public class Test226 {
    private static String value="";
    private static Object lock = new Object();
    static Thread[] tt = new Thread[2];
    static Thread[] tt1 = new Thread[2];
    public static void main(String[] args) {
        for(int i = 0;i<2;i++) {
            tt[i] = new Thread(new Runnable() {
                @Override
                public void run() {
                    // TODO Auto-generated method stub
                    while(true) {
                        setValue();
                    }
                }
            });
            tt[i].setName("生产者"+ i);
            tt[i].start();
            tt1[i] = new Thread(new Runnable() {
                @Override
                public void run() {
                    // TODO Auto-generated method stub
                    while(true) {
                        getValue();
                    }
                }
            });
            tt1[i] .setName("消费者"+i);
            tt1[i] .start();
        }
    }

    public  static void setValue() {
        synchronized (lock) {
            while(!value.equals("")) {//你有货,我就等等
                try {
                    System.out.println(Thread.currentThread().getName()+"  waiting");
                    lock.wait();

                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName()+"  runing");
            value = System.currentTimeMillis()+"------"+System.nanoTime();
            System.out.println(Thread.currentThread().getName()+"set值"+value);
            lock.notify();//我生产好了你那去用
        }
    }

    public static void getValue() {
        synchronized (lock) {
            while(value.equals("")) {//库房没货,你倒是给我呀,我就等
                try {
                    System.out.println(Thread.currentThread().getName()+"  waiting");
                    lock.wait();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            //库房有货,我就拿
            System.out.println(Thread.currentThread().getName()+"  runing");
            System.out.println(Thread.currentThread().getName()+"get值"+System.currentTimeMillis()+"------"+System.nanoTime());
            value="";
            //你通知可以生产了。
            lock.notify();
        }
    }
}

这里写图片描述

在生产者生产完,你通知消费者来拿,而这时,获得线程执行权,又被生产者拿,同样的事发生在消费者身上,如果运行中多次出现,所有的线程状态都变成了wait,就是所谓的假死状态。出现假死的原因,就是在唤醒的时候,连续唤醒同类,解决该办法就是使用notify将异类一起唤醒。虽然说notifyall唤醒所有,但是每次只执行一个,不行就换下一个线程。

一生产一消费:操作栈

我们通过模拟一个生产者往List最后哦那个添加数据,一个消费者往List取数据,始终保持List数据个数不超过1。这种容器最后哦那个数量固定的,是不是有点像数据结构的栈。

package com.example.test;

import java.util.ArrayList;
import java.util.List;

public class Test227 {
    private static List list = new ArrayList<>();
    private static Object lock = new Object();
    public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {

            @Override
            public void run() {
                // TODO Auto-generated method stub
                while(true) {
                    push();
                }
            }
        });
        t1.start();

        Thread t2 = new Thread(new Runnable() {

            @Override
            public void run() {
                // TODO Auto-generated method stub
                while(true) {
                    pop();
                }
            }
        });
        t2.start();

    }
   public static  void push() {
       synchronized (lock) {
           if(list.size()==1) {
                try {
                    lock.wait();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            list.add(Math.random());
            System.out.println("push "+list.size());
            lock.notify();  
    }
   }

   public static  void pop() {
      synchronized (lock) {
          if(list.size()==0) {
               try {
                lock.wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
           }
           list.remove(0);
           System.out.println("pop"+list.size());
           lock.notify();
    }
   }
}

需要注意:该操作中是两个线程一个放数据一个拿数据,所以不存在数据拿空,还拿的错误。所以在本例中使用if和while作为判断条件没差别。

一生产者和多消费者

package com.example.test;

import java.util.ArrayList;
import java.util.List;

public class Test227 {
    private static List list = new ArrayList<>();
    private static Object lock = new Object();
    public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {

            @Override
            public void run() {
                // TODO Auto-generated method stub
                while(true) {
                    push();
                }
            }
        });
        t1.start();

        Thread t2 = new Thread(new Runnable() {

            @Override
            public void run() {
                // TODO Auto-generated method stub
                while(true) {
                    pop();
                }
            }
        });
        t2.start();


        Thread t3 = new Thread(new Runnable() {

            @Override
            public void run() {
                // TODO Auto-generated method stub
                while(true) {
                    pop();
                }
            }
        });
        t3.start();


          Thread t4 = new Thread(new Runnable() {

            @Override
            public void run() {
                // TODO Auto-generated method stub
                while(true) {
                    pop();
                }
            }
        });
        t4.start();
    }
   public static  void push() {
       synchronized (lock) {
           while(list.size()==1) {
                try {
                    lock.wait();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            list.add(Math.random());
            System.out.println("push "+list.size());
            lock.notifyAll();   
    }
   }

   public static  void pop() {
      synchronized (lock) {
          while(list.size()==0) {
               try {
                lock.wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
           }
           list.remove(0);
           System.out.println("pop"+list.size());
           lock.notifyAll();
    }
   }
}

还是对List的操作,还是保证List只有一个数据活动。在这个例子中,由于等待需要条件判断,如果你将wait放置在if中,就可能出现删除异常,所以我们建议在wait等待条件应该用while来判断,while循环会在线程睡眠前后都检查wait的条件,而if不会。另外在该线程中存在两者一个生产一个消费,如果唤醒的是同种线程,由于条件,他们都是处于等待,最后造成假死。

总结:上面通过两个例子,来阐述了对wait和notify使用的注意点,一个是生产消费,还有一个是控制对List中数据最大容量的操作,希望能对大家有所帮助。

猜你喜欢

转载自blog.csdn.net/venus321/article/details/79677875
今日推荐