经典多线程实例:生产者消费者问题

大部分人在理解Java的多线程这个问题有点困难,尤其是一些刚开始接触的编程语言是C语言的人。考虑到用C语言的人接触的都是一个主线程,下面给出的例子并没有太复杂的结构。主要是用来理解多线程的机制。


采用的例子:生产者消费者问题。

SyncStack存储WoTou

Producer生产WoTou

Consumer消费WoTou


package com.example.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(c).start();
	}

}


class WoTou {
	int id;

	public WoTou(int id) {
		super();
		this.id = id;
	}

	@Override
	public String toString() {
		return "WoTou [id=" + 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.notify();
		arrWT[index] = wt;
		index++;
	}
	
	public synchronized WoTou pop() {
		while(index == 0) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		this.notify();
		index--;
		return arrWT[index];
	}
}


class Producer implements Runnable {
	SyncStack ss = null;

	public Producer(SyncStack ss) {
		super();
		this.ss = ss;
	}

	@Override
	public void run() {
		for(int i=0; i<20; i++) {
			WoTou wt = new WoTou(i);
			ss.push(wt);
			System.out.println("生产:" + wt);
		}
		
	}

}

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

	@Override
	public void run() {
		for(int i=0; i<20; i++) {
			WoTou wt = ss.pop();
			System.out.println("消费:" + wt);
		}
	}
	
}

/*
 wait(),notify(),notifyAll()方法来自java.lang.Object

wait()
public final void wait()
                throws InterruptedException在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。换句话说,此方法的行为就好像它仅执行 wait(0) 调用一样。 
当前线程必须拥有此对象监视器。该线程发布对此监视器的所有权并等待,直到其他线程通过调用 notify 方法,或 notifyAll 方法通知在此对象的监视器上等待的线程醒来。然后该线程将等到重新获得对监视器的所有权后才能继续执行。 

对于某一个参数的版本,实现中断和虚假唤醒是可能的,而且此方法应始终在循环中使用: 

synchronized (obj) {
while (<condition does not hold>)
obj.wait();
... // Perform action appropriate to condition
     }
 此方法只应由作为此对象监视器的所有者的线程来调用。有关线程能够成为监视器所有者的方法的描述,请参阅 notify 方法。 

抛出: 
IllegalMonitorStateException - 如果当前线程不是此对象监视器的所有者。 
InterruptedException - 如果在当前线程等待通知之前或者正在等待通知时,任何线程中断了当前线程。在抛出此异常时,当前线程的中断状态 被清除。
另请参见:
notify(), notifyAll()





notify
public final void notify()
唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。选择是任意性的,并在对实现做出决定时发生。线程通过调用其中一个 wait 方法,在对象的监视器上等待。 
直到当前线程放弃此对象上的锁定,才能继续执行被唤醒的线程。被唤醒的线程将以常规方式与在该对象上主动同步的其他所有线程进行竞争;例如,唤醒的线程在作为锁定此对象的下一个线程方面没有可靠的特权或劣势。 

此方法只应由作为此对象监视器的所有者的线程来调用。通过以下三种方法之一,线程可以成为此对象监视器的所有者: 

通过执行此对象的同步实例方法。 
通过执行在此对象上进行同步的 synchronized 语句的正文。 
对于 Class 类型的对象,可以通过执行该类的同步静态方法。 
一次只能有一个线程拥有对象的监视器。 


抛出: 
IllegalMonitorStateException - 如果当前线程不是此对象监视器的所有者。
另请参见:
notifyAll(), wait()





notifyAll
public final void notifyAll()唤醒在此对象监视器上等待的所有线程。线程通过调用其中一个 wait 方法,在对象的监视器上等待。 
直到当前线程放弃此对象上的锁定,才能继续执行被唤醒的线程。被唤醒的线程将以常规方式与在该对象上主动同步的其他所有线程进行竞争;例如,唤醒的线程在作为锁定此对象的下一个线程方面没有可靠的特权或劣势。 

此方法只应由作为此对象监视器的所有者的线程来调用。有关线程能够成为监视器所有者的方法的描述,请参阅 notify 方法。 


抛出: 
IllegalMonitorStateException - 如果当前线程不是此对象监视器的所有者。
另请参见:
notify(), wait()
*/


运行结果我这不给出,大家可以运行下。加深一下理解。


注:while(index == 0),while(index == arrWT.length)两条语句不可以用if(index == 0),if(index == arrWT.length)

原因是如果在wait方法被打断的时候,不再检查临界值,用while循环可以一直很好的控制边界问题。

在编程以及写项目的时候特别需要注意这两个关键字的区别。



由于本人的水平有限,很多问题并没有给出太多的解释,如果你有任何疑问,希望你可以留言告诉我。

发布了35 篇原创文章 · 获赞 29 · 访问量 32万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览