天天看点

多线程实践-生产者消费者

        当多个线程操作同一个资源,但是操作的动作不同时,就会需要线程间进行通信。很著名的是生产者消费者的例子。

        有一个盘子,只能放一片面包,生产者生产面包放入盘子,消费者从盘子中取走面包吃掉。

        由简单开始,i+1。先看一个生产者、一个消费者。

代码如下:

public class ProducerConsumerDemo {
    public static void main(String[] args){
        Resource r = new Resource();

        new Thread(new Producer(r)).start();
        new Thread(new Consumer(r)).start();
    }
}

class Resource{  //公共资源
    private String name;
    private int count =1;
    private boolean flag = false;

    public synchronized void set(String name){
        if(flag)
            try{this.wait();}catch(Exception e){}
        this.name = name + "--" + count++;
        System.out.println(Thread.currentThread().getName() + "...生产者:生产了"+this.name);
        flag = true;
        notify();
    }
    public synchronized void out(){
        if(!flag)
            try{this.wait();}catch(Exception e){}
        System.out.println(Thread.currentThread().getName() + "...消费者:消费了"+this.name);
        flag = false;
        notify();
    }
}
class Producer implements Runnable{
    private Resource res;
    Producer(Resource res){
        this.res = res;
    }
    public void run(){
        while(true){
            res.set("面包");
        }
    }
}
class Consumer implements Runnable{
    private Resource res;
    Consumer(Resource res){
        this.res = res;
    }
    public void run(){
        while(true){
            res.out();
        }
    }
}
           

        运行结果如图:

多线程实践-生产者消费者

        由运行结果可以看到。Thread-0和Tread-1两个线程是交替进行,生产者生产商品i,消费者就把商品i消费掉。然后生产者生产商品i+1,消费者再消费商品i+1。

        本来我自己想的实现过程是这样的:对于盘子来说,它有两种状态,可以往里放面包和不可以放面包。生产者来了,如果可放,就生产一个面包,并把盘子置为不可放面包的状态,如果不可放,就什么都不操作。消费者来了,如果可放(就代表不能取),就什么都不操作,如果不可放(代表能取),就取走一个面包,并把盘子置为可放面包状态。代码如下:

class Resource{  //公共资源
    private String name;
    private int count =1;
    private boolean flag = false;

    public synchronized void set(String name){
        if(flag)
        {
        }
          //try{this.wait();}catch(Exception e){}
        else{
            this.name = name + "--" + count++;
            System.out.println(Thread.currentThread().getName() + "...生产者:生产了"+this.name);
            flag = true;
        }
        //notify();
    }
    public synchronized void out(){
        if(!flag){
        }
        //try{this.wait();}catch(Exception e){}
        else{
            System.out.println(Thread.currentThread().getName() + "...消费者:消费了"+this.name);
            flag = false;
        }

        //notify();
    }
}
           

        与上面的示例中代码的区别是没有使用wait()和notify()方法。一样能实现效果。看图:

多线程实践-生产者消费者

        不用使用wait()和notify()与使用有什么区别呢?既然它存在,肯定是有它的道理的。猜测它的优点是效率更高。用什么方法可以验证一下?尝试着加了运行时间和打印输出。如图:

        (1)不用wait()/notify()

多线程实践-生产者消费者

        (2)用wait()/notify()

多线程实践-生产者消费者

        count为10000时,不使用wait()和notify()生产9999个面包需要1330ms,而使用wait()和notify()生产9999个面包只需要406ms。多次执行,每次的结果相差不大。

增加一下数量级,再比比,也很明显:6704msVS 3208ms。

多线程实践-生产者消费者
多线程实践-生产者消费者

(1)不用wait()/notify()(2)用wait()/notify()                   

        计时代码增加到了main方法所在类和公共资源类下,代码如下:

public class ProducerConsumerDemo {
    public static long strateTime;
    public static void main(String[] args){
        Resource r = new Resource();
        strateTime = System.currentTimeMillis();
        new Thread(new Producer(r)).start();
        new Thread(new Consumer(r)).start();
    }
}

class Resource{  //公共资源
    public static long endTime;
    private String name;
    private int count =1;
    private boolean flag = false;

    public synchronized void set(String name){
        if(flag)
//        {
//           System.out.println("无效执行");
//        }
            try{this.wait();}catch(Exception e){}
        else{
            this.name = name + "--" + count++;
            if(count==100000){
                endTime= System.currentTimeMillis();
                System.out.println("程序运行时间:" + ( endTime - ProducerConsumerDemo.strateTime )+"ms");
            }
            System.out.println(Thread.currentThread().getName() + "...生产者:生产了"+this.name);
            flag = true;
        }
        notify();
    }
    public synchronized void out(){
        if(!flag)
//        {
//            System.out.println("无效执行");
//        }
            try{this.wait();}catch(Exception e){}
        else{
            System.out.println(Thread.currentThread().getName() + "...消费者:消费了"+this.name);
            flag = false;
        }

        notify();
    }
}
           

        对比发现,差不多两倍的差距,效率是不一样的。是因为啥呢?

        //TODO:

多线程方法汇总:

        等待唤醒:wait()、notify()、notifyAll()。

        waite()是把线程由运行状态置为等待状态,等待线程都存在线程池中。

        notify()方法是把等待状态的线程从线程池中唤醒。通常唤醒线程池中的第一个等待的线程。

        notifyAll()是把线程池中的所有等待线程都唤醒。

继续阅读