Java复习(十三)----多线程(三)

生产者消费者问题

现在我们要写一个程序,来模拟做馒头和吃馒头,一个不断的往篮子里扔做熟的馒头,一个不断的在那吃。模型如下:
在这里插入图片描述
现在要求是,写一个程序来模拟这个过程,面向对象,怎么写呢?

我们要写考虑里面有哪些类呢?我们分析名字就行了

首先我们先把主要的类和main方法写一下:

public class ProducerConsumer {
    public static void main(String[] args) {
    }
}

然后写一下馒头类:

 class WoTou {
    int id;
    WoTou(int id) {
        this.id = id;
    }
    public String toString() {
    return "WoTou : " + id;
	}
}

给他一个id,可以更好地看清哪个馒头做出来了,那个馒头被吃了。

接下来是篮子类。我们想一下,我们先做的馒头是放在底层的是吧,别人要吃都是从上面开始拿的,先进后出,栈。

class SyncStack {
    int index = 0;//装到第几个了
    WoTou[] arrWT = new WoTou[6];//这个篮子能装6个馒头

	//装馒头的方法
    public synchronized void push(WoTou wt) {
        arrWT[index] = wt;
        index ++;//放了馒头,肯定要占一个位置的
    }

要是上面程序不加锁会怎样呢?
当你在一个位置装了馒头之后,有另外一个线程,放馒头的人也好,吃馒头的人也好,都是一个线程。你往里扔馒头的时候,有一个人已经扔进去了,可是他被打断了,index还没来得及往上加,下一个人又往里扔,会产生什么问题啊,会把原来的馒头给覆盖掉。index还是0。怎么解决呢?加个锁就好了!

还有一个问题就是,如果篮子满了怎么办呢?我们是不是得休息一下啊。所以要进行线程控制,用wait方法

    public synchronized void push(WoTou wt) {
        while(index == arrWT.length) {
            try {
                this.wait();//指的是SyncStack,让生产者休息
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        arrWT[index] = wt;
        index ++;
    }

wait的解释是,当前的,正在我这个对象访问的线程。
一个线程访问push()方法的时候,他已经拿到这个对象的锁了,拿到对象这个锁的线程,在执行的过程之中,他遇见一个事件必须阻塞住,必须停止。什么事件,已经满了,你不能往里填了。必须等清洁工把它清走,才可以继续。没清走之前,你只能等着

这里我们说一下wait和sleep的区别:

上面代码是不行的,为什么不行呢,因为这个东西全等在那了,第一个人生产满了,他就在那等住了,等住了之后,另外一个人就算消费空了,篮子里边没有馒头了,可是他依然运行不了。为什么呀?因为没有人叫醒他


wait时别的线程可以访问锁定对象。调用wait方法的时候必须锁定该对象。
sleep时别的线程也不可以访问锁定对象


wait和sleep不一样。sleep过一段时间他会自己醒过来,可是wait不行。wait一旦wait过去,对不起,死过去了。并且,wait一旦wait过去了,这个对象的锁不在归我所有,只有醒过来的时候才会再去找这把锁。这是wait和sleep之间巨大的区别。wait的时候,锁不在归我所有。sleep的时候,睡着了也得抱着那把锁。

wait完之后我们要干嘛呢?我们要做一件事,就是叫消费者赶快消费是吧,这样篮子才会有空位置。这时我们就要用notify方法了。wait方法和notify方法是一一对应的。所以我们就得在代码中加上this.notify()。当有多个线程就得用this.notiyAll()

notify的含义:叫醒一个正在wait在我这个对象上的线程。this.notify(),谁现在正在我这个对象等待着,我就叫醒谁,让它继续执行

那我们既然有放馒头的方法,肯定也有拿馒头的方法是吧:

  public synchronized WoTou pop() {
        index--;//拿了馒头,便把位置数-1
        return arrWT[index];
    }

pop方法也一样,当篮子里的馒头空了之后,就必须等待着

public synchronized WoTou pop() {
        while(index == 0) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        index--;
        return arrWT[index];
    }

接下来我们还得有生产者这个类吧,不然馒头哪里来?他是一个线程,因为好多人在一起做馒头。

class Producer implements Runnable {
    SyncStack ss = null;//生产者得知道往哪个框生产吧,所以得引用一个对象。
    Producer(SyncStack ss) {
        this.ss = ss;
    }

有了线程,就得有启动线程的方法

    public void run() {
        for(int i=0; i<20; i++) {
            WoTou wt = new WoTou(i);
            ss.push(wt);
            System.out.println("生产了:" + wt);
            try {
                Thread.sleep((int)(Math.random() * 1000));
                //为了方便观察,每生产一个,便睡眠一会
                //Math.random()是一个double类型,这里强制转换为int类型
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

有生产者的类,自然也就有消费者的类:

class Consumer implements Runnable {
    SyncStack ss = null;
    Consumer(SyncStack ss) {
        this.ss = ss;
    }
}

消费者这个线程自然也有启动的方法:

public void run() {
        for(int i=0; i<20; i++) {
            WoTou wt = ss.pop();
            System.out.println("消费了: " + wt);
            try {
                Thread.sleep((int)(Math.random() * 1000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

接下来我们就得模拟一下线程了。首先得造一个框出来装馒头吧。

public class ProducerConsumer {
    public static void main(String[] args) {
        SyncStack ss = new SyncStack();
        Producer p = new Producer(ss);
        Consumer c = new Consumer(ss);
        new Thread(p).start();
        new Thread(c).start();
    }
}

这里只模拟了一个生产者和一个消费者。

下面来看看完整代码:

package Thread;

public class ProducerConsumer {
    public static void main(String[] args) {
        SyncStack ss = new SyncStack();
        Producer p = new Producer(ss);
        Consumer c = new Consumer(ss);
        new Thread(p).start();
        new Thread(p).start();
        new Thread(c).start();
        new Thread(c).start();
    }
}

    class WoTou {
    int id;
    WoTou(int id) {
        this.id = id;
    }
    public String toString() {
        return "WoTou : " + id;
    }
}

class SyncStack {
    int index = 0;
    WoTou[] arrWT = new WoTou[6];

    public synchronized void push(WoTou wt) {
        while(index == arrWT.length) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.notifyAll();
        arrWT[index] = wt;
        index ++;
    }

    public synchronized WoTou pop() {
        while(index == 0) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.notifyAll();
        index--;
        return arrWT[index];
    }
}

class Producer implements Runnable {
    SyncStack ss = null;
    Producer(SyncStack ss) {
        this.ss = ss;
    }

    public void run() {
        for(int i=0; i<20; i++) {
            WoTou wt = new WoTou(i);
            ss.push(wt);
            System.out.println("生产了:" + wt);
            try {
                Thread.sleep((int)(Math.random() * 1000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

class Consumer implements Runnable {
    SyncStack ss = null;
    Consumer(SyncStack ss) {
        this.ss = ss;
    }

    public void run() {
        for(int i=0; i<20; i++) {
            WoTou wt = ss.pop();
            System.out.println("消费了: " + wt);
            try {
                Thread.sleep((int)(Math.random() * 1000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

多线程(三)也写到这啦,这个章节比较简单,就说了一下生产者和消费者的问题,也说了wait和sleep的区别,这个是重点!

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值