生产者消费者模型
两个进程共享一个缓冲区,一个进程向缓冲区中写入数据(生产者),另一个进程从缓冲区中读取数据(消费者)。当缓冲区被写满时,生产者就必须进入挂起状态,直到消费者从缓冲区中取走数据时,生产者才能继续向缓冲区中放数据。同样当缓冲区没有数据时,消费者进程就必须进入挂起状态,直到生产者向缓冲区中放入数据时,消费者才能被唤醒继续从缓冲区中取走数据。
先实现一个队列:
public class ArrayQueue {
private int[] array = new int[10];
private int size = 0;
private int front = 0;
private int rear = 0;
public void put(int val){
if(size == array.length){
throw new RuntimeException("队列已满");
}
array[rear++] = val;
if(rear == array.length){
rear = 0;
}
size++;
}
public int take(){
if(size == 0){
throw new RuntimeException("队列空");
}
int val = array[front];
front = (front+1)%array.length;
size--;
return val;
}
public int getSize(){
return size;
}
1.创建生产者、消费者类并集成线程,通过Thread的构造方法命名两个线程;
2.通过PrintWriter将生产的元素和消费的元素写到文本中用于比对;
3.覆写run()方法,一个调用put()进行生产,一个调用take()进行消费;
private static ArrayQueue queue = new ArrayQueue();
private static class Producer extends Thread {
Producer(){
super("生产者");
}
PrintWriter printWriter;
{
try {
printWriter = new PrintWriter("生产了.txt", "UTF-8");
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
Random random = new Random(20191216);
for (int i = 0; i < 5000; i++) {
int val = random.nextInt(100);
System.out.println(val);
printWriter.println(val);
do {
try {
queue.put(val);
break;
} catch (RuntimeException e) {
}
}while (true);
}
printWriter.close();
}
}
private static class Customer extends Thread{
Customer(){
super("消费者");
}
PrintWriter printWriter;
{
try {
printWriter = new PrintWriter("消费了.txt","UTF-8");
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
for (int i = 0; i < 5000; i++) {
do {
try {
int val = queue.take();
printWriter.println(val);
break;
} catch (RuntimeException e) {
}
}while (true);
}
printWriter.close();
}
}
public static void main(String[] args) {
Producer producer = new Producer();
producer.start();
Customer customer = new Customer();
customer.start();
}
单生产者、单消费者
private int[] array = new int[10];
private volatile int size = 0;
private int front = 0;
private int rear = 0;
public void put(int val) throws InterruptedException {
if(size == array.length){
synchronized (this){
wait();
}
}
array[rear++] = val;
if(rear == array.length){
rear = 0;
}
synchronized (this) {
size++;
notify();
}
}
public int take() throws InterruptedException {
if(size == 0){
synchronized (this){
wait();
}
}
int val = array[front];
front = (front+1)%array.length;
synchronized (this) {
size--;
notify();
}
return val;
}
public int getSize(){
return size;
}
private static ArrayQueue queue = new ArrayQueue();
private static class Producer extends Thread {
Producer(){
super("生产者");
}
@Override
public void run() {
Random random = new Random(20191216);
for (int i = 0; i < 5000; i++) {
int val = random.nextInt(100);
try {
queue.put(val);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
private static class Customer extends Thread{
Customer(){
super("消费者");
}
@Override
public void run() {
for (int i = 0; i < 5000; i++) {
try {
int val = queue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws InterruptedException {
Producer producer = new Producer();
producer.start();
Customer customer = new Customer();
customer.start();
}
如何保证生产者、消费者模型的线程安全?
1、用volatile保证size变量的有序性从而保证线程安全;
2、size==0 或 size==array.length 时调用wait()方法,使线程进入等待集;
3、当size++或size--时通过synchronize锁住当前引用,并且调用notify()唤醒生产者或消费者,保证线程安全;
注意:多消费者或多生产者时线程安全没法保证!
最终版
private int[] array = new int[10];
private volatile int size = 0;
private int front = 0;
private int rear = 0;
//在take()和put()方法上加synchronize,效率低
public synchronized void put(int val) throws InterruptedException {
//用while()语句判断size的值
while (size == array.length){
wait();
}
array[rear++] = val;
if(rear == array.length){
rear = 0;
}
synchronized (this) {
size++;
//调用notifyAll唤醒所有线程
notifyAll();
}
}
public synchronized int take() throws InterruptedException {
while (size == 0){
wait();
}
int val = array[front];
front = (front+1)%array.length;
synchronized (this) {
size--;
notifyAll();
}
return val;
}
public int getSize(){
return size;
}
private static ArrayQueue queue = new ArrayQueue();
private static class Producer extends Thread {
Producer(){
super("生产者");
}
@Override
public void run() {
Random random = new Random(20191216);
while (true){
int val = random.nextInt(100);
try {
queue.put(val);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
private static class Customer extends Thread{
Customer(){
super("消费者");
}
@Override
public void run() {
while (true){
try {
int val = queue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws InterruptedException {
Producer producer = new Producer();
producer.start();
for (int i = 0; i < 3; i++) {
Customer customer = new Customer();
customer.start();
}
while (producer.isAlive()){
System.out.println(ArrayQueue.queue.getSize());
TimeUnit.MILLISECONDS.sleep(1);
}
}
1、当消费者和生产者线程都进入等待集,唤醒是随机的,因此使用notifyAll()唤醒全部线程,while()的作用也在此;
2、synchronize是为了防止在while语句判断完后立马被抢走CPU,从而导致线程不安全;