普通的生产者消费者模式:
package ss;
/**
* 线程通讯实例:
*
* 生产者消费者模式
*
*/
//产品类
class Product{
String name;//名字
double price;//价格
boolean flag=false;//产品是否生产完毕的标签,默认情况是没有生产完成。
}
//生产者
class Producer extends Thread{
//产品
Product p;
public Producer(Product p){
this.p=p;
}
@Override
public void run() {
int i=0;
while(true){
synchronized (p) {
if(p.flag==false){
if(i%2==0){
p.name="苹果";
p.price=6.5;
}else{
p.name="香蕉";
p.price=2.0;
}
System.out.println("生产者生产出了: "+p.name+"价格是: "+p.price);
p.flag=true;
i++;
p.notifyAll();//唤醒消费者去消费
}else{
//已经生产完毕,等待消费者先去消费
try {
p.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
//消费者
class Customer extends Thread{
Product p;
public Customer(Product p){
this.p=p;
}
@Override
public void run() {
while(true){
synchronized (p) {
if(p.flag==true){//产品已经生产完毕
System.out.println("消费者消费了"+p.name+"价格: "+p.price);
p.flag=false;
p.notifyAll();//唤醒生产者去生产
}else{
//产品还有生产,等待生产者先生产
try {
p.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
}
public class Demo {
public static void main(String[] args) {
Product p=new Product();//产品
//创建生产者
Producer producer=new Producer(p);
//创建消费
Customer customer=new Customer(p);
//调用start方法开启线程
producer.start();
customer.start();
}
}
队列实现生产者消费者模式:
- 在Java多线程应用中,队列的使用率很高,多数生产者消费者模型的首选数据结构就是队列(先进先出)。
- Java提供线程安全的Queue可以分为阻塞式队列和非阻塞式队列。
- 阻塞式队列的典型例子就是BlockingQueue.当前线程没有数据或者数据满了的时候会阻塞。
- 非阻塞式队列的典型例子就是LinkedList(双向列表)
/***
* Collection(list,set,queue)
* @author pjy
*
* LinkedList是一个Deque(双重队列)入口可以添加数据,入口也可以移除数据
*/
public class ThreadCommunictaion01 {
public static void main(String[] args)
throws Exception{
List<String> list=
new ArrayList<String>(2);
list.add("a1");
list.add("a2");
list.add("a3");
list.add("a4");
System.out.println("list.size()="+list.size());
//阻塞式队列
final BlockingQueue<String> q1=
new ArrayBlockingQueue<>(3);
new Thread(){
public void run(){
try{
String s=q1.take();//阻塞式方法,没有数据则等待
//String s=q1.remove();//没有数据会抛出异常
System.out.println("s="+s);
}catch(Exception e){
e.printStackTrace();
}
};
}.start();
q1.put("A");
q1.put("B");
q1.put("C");
//当队列满以后,当前线程会阻塞
q1.put("D");//此方法满了会阻塞
//q1.add("A");此方法满了会抛出异常
System.out.println("q1.size()="+q1.size());
}
}
案例:简单的生产者消费者模式:一边放,一边取
- BlockingQueue队列:
import java.util.Scanner;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
/**
*
* 生产者:向容器放数据,此容器从键盘读入字符串
* 存储到容器(阻塞式队列)中
*
*/
class Producer extends Thread{//is a
//has a (关联)
private BlockingQueue<String> queue;
public Producer(BlockingQueue<String> queue) {
this.queue=queue;
}
@Override
public void run() {
Scanner sc=new Scanner(System.in);
while(true){
//从键盘读入一个字符串
System.out.println("放数据:");
String s=sc.nextLine();
try{
//将字符串放入容器
queue.put(s);
}catch(Exception e){e.printStackTrace();}
}
}
}
/**消费者:从容器中取数据*/
class Consumer extends Thread{
//has a (关联)
private BlockingQueue<String> queue;
public Consumer(BlockingQueue<String> queue) {
this.queue=queue;
}
@Override
public void run() {
while(true){
try{
System.out.println("take:"+queue.take());
}catch(Exception e){e.printStackTrace();}
}
}
}
public class ThreadCommunication02 {
public static void main(String[] args) {
//容器
BlockingQueue<String> container=new LinkedBlockingQueue<String>(3);
//构建线程对象
Producer p=new Producer(container);
Consumer c=new Consumer(container);
//启动线程
p.start();
c.start();
//一边放一边取(简单地生产者消费者模式)
}
}
- LinkedList队列:
import java.util.LinkedList;
import java.util.Scanner;
import java.util.concurrent.BlockingQueue;
/**生产者向容器放数据,此容器从键盘读入字符串
* 存储到容器(非阻塞式队列)中*/
class Producer02 extends Thread{//is a
//has a (关联)
private LinkedList<String> queue;
public Producer02(LinkedList<String> queue) {
this.queue=queue;
}
@Override
public void run() {
Scanner sc=new Scanner(System.in);
while(true){
//从键盘读入一个字符串
System.out.println("放数据:");
String s=sc.nextLine();//阻塞方法
synchronized (this) {
try{
//将字符串放入容器
queue.addLast(s);
//通知消费者取
queue.notify();
}catch(Exception e){e.printStackTrace();}
}
}
}
}
/**消费者从容器中取数据*/
class Consumer02 extends Thread{
//has a (关联)
private LinkedList<String> queue;
public Consumer02(LinkedList<String> queue) {
this.queue=queue;
}
@Override
public void run() {
while(true){
synchronized (this) {
//容器内容为空时则等待
if(queue.size()==0)try{queue.wait();}catch(Exception e){}
//取数据
try{
System.out.println("take:"+queue.removeFirst());
}catch(Exception e){e.printStackTrace();}
//通知消费者继续方法
//queue.notify();
}
}
}
}
public class ThreadCommunication03 {
public static void main(String[] args) {
//容器
LinkedList<String> container=
new LinkedList<String>();
//构建线程对象
Producer02 p=new Producer02(container);
Consumer02 c=new Consumer02(container);
//启动线程
p.start();
c.start();
}
}
- ArrayList:
class Producer03 extends Thread{
@Override
public void run() {
Scanner sc=new Scanner(System.in);
while(true){
String data=sc.nextLine();
//放数据
}
}
}
class Consumer03 extends Thread{
@Override
public void run() {
while(true){
//取数据
}
}
}
class Container03{
private List<String> list;
/**@param 容量*/
public Container03(int cap) {
list=new ArrayList<>(cap);
}
/**放数据*/
public synchronized void put(String data){
//1.判断容器是否已满,满则等待
//2.放数据
//3.通知消费者取数据
}
/**取数据*/
public synchronized void take(){
//1.判断容器是否为空,空则等待
//2.取数据
//3.通知生产者放数据
}
}
public class ThreadCommunication04 {
}
wait()与notify()
wait():
等待,如果线程执行了wait方法,那么该线程会进入等待的状态,等待状态下的线程必须要被其他线程调用notify方法才能唤醒。
一个线程如果执行了wait方法,那么该线程就会进去一个以锁对象为标识符的线程池中等待。
notify():
唤醒 唤醒等待的方法。
如果一个线程执行了notify方法,那么会唤醒以锁对象为标识符中等待线程中其中一个。
wait与notify方法要注意的事项:
1、wait方法与notify方法是属于Object对象的。
2、wait方法与notify方法必须要在同步代码块或者是同步函数中才能使用。
3、wait方法与notify方法必须要由锁对象调用。