生产者消费者模型
思路一
通过实际问题构造一个生产者和消费者问题,用馒头类,放馒头的框,生产馒头的厨师类,和吃馒头的消费者类,其中后两者实现Runnable接口,然后通过创建多个线程来生产馒头和消费馒头;
代码实现一
public class Main {
public static void main(String[] args) {
Bucket b = new Bucket(6);
Producer p = new Producer(b);
Consumer c = new Consumer(b);
new Thread(p).start();
new Thread(c).start();
}
}
class Mantou {
@Override
public String toString() {
return "Mantou:" + id;
}
int id;
Mantou(int id) {
this.id = id;
}
}
class Bucket {
int max, index;
Mantou[] m;
Bucket(int max) {
this.max = max;
index = -1;
m = new Mantou[max];
}
synchronized void push(Mantou m1) {
while (index == max-1) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.notify();
m[++ index] = m1;
}
synchronized Mantou pop() {
while (index == -1) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.notify();
return m[index --];
}
}
class Producer implements Runnable {
Bucket b = null;
Producer(Bucket b) {
this.b = b;
}
@Override
public void run() {
for (int i=0; i<20; i++) {
Mantou m = new Mantou(i);
System.out.println("生产了:" + m);
try {
Thread.sleep((int)(Math.random() * 1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
b.push(m);
}
}
}
class Consumer implements Runnable {
Bucket b = null;
Consumer(Bucket b) {
this.b = b;
}
@Override
public void run() {
for (int i=0; i<20; i++) {
Mantou m = b.pop();
System.out.println("消费了:" + m);
}
}
}
思路二
通过创建同步容器来模拟生产者和消费者的问题;
代码实现二
package useful;
import java.util.ArrayList;
import java.util.List;
public class MyContainer<T> {
private final int max = 10;
private final List<T> list = new ArrayList<>();
// private int count = 0;
synchronized void put(T t) {
while (list.size() == max) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.notifyAll();
list.add(t);
// count ++;
}
synchronized T get() {
while (list.size() == 0) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.notifyAll();
// count --;
return list.remove(list.size()-1);
}
public static void main(String[] args) {
MyContainer<String> m = new MyContainer<>();
for (int i=0; i<10; i++) {
new Thread(() -> {
for (int j=0; j<5; j++) System.out.println(Thread.currentThread().getName() + " consumed " + m.get());
}, "c" + i).start();;
}
for (int i=0; i<10; i++) {
new Thread(() -> {
for (int j=0; j<5; j++) {
m.put(Thread.currentThread().getName() + ":" + j);
}
}, "p" + i).start();;
}
}
}
思路三
利用Lock和Condition实现并发访问;可以精确控制到让哪一个或者哪一种线程唤醒;
代码如下
package useful;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class MyContainer<T> {
private final int max = 10;
private final List<T> list = new ArrayList<>();
Lock lock = new ReentrantLock();
private Condition producer = lock.newCondition();
private Condition consumer = lock.newCondition();
void put(T t) {
try {
lock.lock();
while (list.size() == max) {
producer.await();
}
list.add(t);
consumer.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
synchronized T get() {
T t = null;
try {
lock.lock();
while (list.size() == 0) {
consumer.await();
}
t = list.get(list.size()-1);
producer.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
return t;
}
public static void main(String[] args) {
MyContainer<String> m = new MyContainer<>();
for (int i=0; i<10; i++) {
new Thread(() -> {
for (int j=0; j<5; j++) System.out.println(Thread.currentThread().getName() + " consumed " + m.get());
}, "c" + i).start();;
}
for (int i=0; i<10; i++) {
new Thread(() -> {
for (int j=0; j<5; j++) {
m.put(Thread.currentThread().getName() + ":" + j);
}
}, "p" + i).start();;
}
}
}