生产者及消费者的概念
多线程中有一个典型的案例,就是生产者和消费者的问题,生产者不断生产,消费者不断取走生产者生产的产品。即生产者产出信息后放入一个区域,消费者从该区域取走数据,但是会涉及到两个问题:
1. 生产者刚向存储空间添加了信息的名称还没来得及放内容,线程就切换到消费者线程了,消费者线程会把新的信息名称和上一次的信息内容联系到一起。
2. 生产者放入了好几条数据之后消费者才开始取数据,导致数据的浪费,或者生产者还没放数据,消费者就取了好几次,导致数据的重复。
程序的基本实现
如果想让生产者不重复生产,消费者不重复取走,则可以增加一个布尔类型的标志位,如果标志位内容为false则生产,但是不能取走,此时线程执行到了消费者线程应该等待,如果标志位为true,表示可以取走,但是不能生产,生产者线程运行应当等待。这里涉及到两个方法wait()和notify();
wait()方法是使一个线程进入睡眠状态,如果没有被唤醒则一直睡下去,和sleep()不同的是,wait()被执行之后,该线程交出线程的执行权。
notify()是唤醒线程池中的任意一个线程。
具体示例代码如下:
生产者代码:
public class Producer implements Runnable{
Resource r;
public Producer(Resource r){
this.r = r;
}
@Override
public void run() {
while(true){
r.set();
}
}
}
消费者代码:
public class Consumer implements Runnable{
private Resource r;
public Consumer(Resource r){
this.r = r;
}
@Override
public void run() {
while(true){
r.get();
}
}
}
资源代码,即存放信息的地方:
public class Resource {
private boolean flag = false;
private int count = 1;
public synchronized void get(){
if(!flag){
try {
this.wait();
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+ "消费了蛋糕"+count);
count++;
flag = false;
notify();
}
public synchronized void set(){
if(flag){
try {
this.wait();
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+ "生产了蛋糕"+count);
flag = true;
notify();
}
}
主方法:
public class MainThread {
public static void main(String[] args) {
Resource r = new Resource();
Producer p = new Producer(r);
Consumer c = new Consumer(r);
Thread t1 = new Thread(p);
Thread t2 = new Thread(c);
t1.start();
t2.start();
}
}
运行之后的结果为:
...
Thread-0生产了蛋糕108
Thread-1消费了蛋糕108
Thread-0生产了蛋糕109
Thread-1消费了蛋糕109
Thread-0生产了蛋糕110
Thread-1消费了蛋糕110
Thread-0生产了蛋糕111
Thread-1消费了蛋糕108
Thread-0生产了蛋糕109
Thread-1消费了蛋糕109
Thread-0生产了蛋糕110
Thread-1消费了蛋糕110
Thread-0生产了蛋糕111
Thread-1消费了蛋糕111
...
这样就实现了生产者和消费者,但是这种情况只是对于1个生产者和1个消费者的情况。
当有多个生产者和多个消费者的情况就又不一样了,对于生产者而言发现如果装货物的架子没满就继续生产,而对消费者而言,如果装货物的架子没空就进行消费,所以如果面对多个生产者和消费者Resource代码应改为:
public class Resource {
private boolean flag = false;
private int count = 1;
public synchronized void get(){
while(!flag){ //循环判断
try {
this.wait();
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+ "消费了蛋糕"+count);
count++;
flag = false;
notifyAll();//唤醒所有线程
}
public synchronized void set(){
while(flag){ //循环判断
try {
this.wait();
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+ "生产了蛋糕"+count);
flag = true;
notifyAll();//唤醒所有线程
}
}