微信搜索:小大白日志

生产者与消费者

Java中一共有四种方法支持同步,实现生产者消费者模型,不管哪一种,都是用Queue作为缓冲队列(常用前三种):
(1)Object的wait() / notify()方法
(2)Lock和Condition的await() / signal()方法
(3)BlockingQueue阻塞队列方法
(4)PipedInputStream / PipedOutputStream
 
 
  • wait()/ nofity()方法是基类Object的两个方法,所有Java类都会拥有这两个方法,这样我们就可以为任何对象实现同步机制。wait(),当缓冲区已满/空时,生产者/消费者线程停止自己的执行,放弃锁,使自己处于等待状态,让其他线程执行。notify()/notifyAll(),当生产者/消费者向缓冲区放入/取出一个产品时,向其他等待获取该临资的线程发出可执行的通知,同时放弃锁,使自己处于等待状态。
//启动类
public class ProductConsumer{
    public static void main(String[] args) throws InterruptedException{
        Queue<Integer> queue=new LinkedList<Integer>();
        Product product=new Product(queue,3,"生产者-1");
        //此处还可以设置更多生产者...
        Consumer consumer=new Consumer(queue,3,"消费者-1");
        //此处还可以设置更多消费者...
        Thread t1=new Thread(product);
        Thread t2=new Thread(consumer);
        t1.start();
        t2.start();
    }
}

//生产者
public class Product implements Runnable {
    private String name;
    private int now=1;
    private Queue queue;
    private int maxSize; 
    public Product(Queue q,int max,String name){
        this.name=name;
        this.queue=q;
        this.maxSize=max;
    }
    @Override
    public void run() {
        while(true){
            synchronized(queue){
                while(queue.size()==maxSize){
                System.out.println("队列满了,"+this.name+"等待...");
                    try {
                        queue.wait();
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
                System.out.println(this.name+"生产:"+now);
                queue.offer(now++);
                queue.notifyAll();
                try {
                //此步很重要,生产者生产完必须等待一段时间以被消费
                    Thread.sleep(new Random().nextInt(1000));
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }
}

//消费者
public class Consumer implements Runnable {
    private String name;
    private Queue queue;
    private int maxSize; 
    public Consumer(Queue queue,int max,String name){
        this.name=name;
        this.queue=queue;
        this.maxSize=max;
    }
    @Override
    public void run() {
        while(true){
            synchronized(queue){
                while(queue.isEmpty()){
                try {
                     System.out.println("队列为空,"+this.name+"等待...");
                     queue.wait();
                     } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                     }
                }
                int c=(Integer) queue.poll();
                System.out.println("消费者"+this.name+"吃了:"+c);
                queue.notifyAll();
                try {
                    //此步很重要,消费者消费完必须等待一段时间以被生产
                    Thread.sleep(new Random().nextInt(1000));/
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }
}    

 

 

  • 使用Lock和Condition的await() / signal()方法,await() 让线程"条件等待",并自动释放Lock锁,signal()/signalAll()对线程"条件唤醒",并重新获得锁
(以下模拟存取款同步操作)
//启动类
public class Test {
    private static Account account=new Account(0,20);//初始余额0元,账户最大存储余额20元
    public static void main(String[] args){
        ExecutorService executorService=Executors.newCachedThreadPool();
        executorService.execute(new Deposit(account));
        executorService.execute(new Withdraw(account));
        executorService.shutdown();
    }
}
//账户
public class Account {
    private static int balance=0;//账户余额
    private static int maxBalance=0;//该账户最大存储余额maxBalance元
    private static Lock lock=new ReentrantLock();
    private static Condition lessMoney=lock.newCondition();//条件:余额不够
    private static Condition fullMoney=lock.newCondition();//条件:账户余额超过20元
    public Account(int b,int max){
        this.balance=b;
        this.maxBalance=max;
    }
    public void deposit(int amount){
        lock.lock();
        try{
            while(balance+amount>maxBalance){
                System.out.println("余额超额!现余"+balance+"元,最大存款额"+maxBalance+"元,存入后余"+(balance+amount)+"元,等待取款...");
                fullMoney.await();//此锁条件等待
            }
            balance+=amount;
            lessMoney.signalAll();//此锁条件唤醒
            System.out.println("存入"+amount+"元!余"+balance+"元");
        }catch (InterruptedException e) {
            e.printStackTrace();
        }finally{
            lock.unlock();
        }
    }
    public void withdraw(int amount){
        lock.lock();
        try{
            while(amount>balance){
                System.out.println("余额不够:余"+balance+"元,"+"取"+amount+"元,等待存款...");
                lessMoney.await();
            }
            balance-=amount;
            fullMoney.signalAll();
            System.out.println("取出"+amount+"元!余"+balance+"元");
        }catch(InterruptedException e){
            e.printStackTrace();
        }finally{
            lock.unlock();
        }
    
    }
}

//存款线程(生产者)
public class Deposit implements Runnable {
    private Account account;
    public Deposit(Account a){
        account=a;
    }
    @Override
    public void run() {
        while(true){
            try {
                account.deposit((int)(Math.random()*10+1));//每次存入1-10元
                Thread.sleep(new Random().nextInt(1000));
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}

//取款线程(消费者)
public class Withdraw implements Runnable {
    private Account account;
    
    public Withdraw(Account a){
        this.account=a;
    }
    @Override
    public void run() {
        while(true){
            try {
                account.withdraw((int)(Math.random()*10+1));//每次取出1-10元
                Thread.sleep(new Random().nextInt(1000));
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}

 

  • 使用BlockingQueue阻塞队列
可以使用LinkedBlockingQueue,它是内部已实现了同步的队列,实现方式采用的是await()/ signal()方法。它可以在生成对象时指定容量大小,入队put()队列满了自动阻塞,出队take()队列为空自动阻塞。
 
//启动类
public class Test {
    public static void main(String[] args){
        BlockingQueue<Integer> queue=new LinkedBlockingQueue<Integer>(3);//指定队列容量3
        Product product=new Product(queue,"生产者-1");
        Consumer consumer=new Consumer(queue,"消费者-1");
        product.start();
        consumer.start();
    }
}

//生产者
public class Product extends Thread {
    private static BlockingQueue queue;
    private static int i=0;
    private String name;
     public Product(BlockingQueue q,String n){
         this.queue=q;
         this.name=n;
     }
     public void run(){
             try {
                 while(true){
                queue.put(i);
                System.out.println(this.name+"生产:"+i);
                 i++;
                //此步很重要,生产者生产完必须等待一段时间以被消费
                Thread.sleep(new Random().nextInt(1000));//最多1s
                 }
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
         }
     }
}


//消费者
public class Consumer extends Thread{
    private static BlockingQueue queue;
    private String name;
    public Consumer(BlockingQueue q,String n){
        this.queue=q;
        this.name=n;
    }
    public void run(){
            try {
                while(true){
                    int i=(Integer) this.queue.take();
                    System.out.println(this.name+"吃了:"+i);
            //此步很重要,消费者消费完必须等待一段时间以被生产 Thread.sleep(
new Random().nextInt(2000)); } } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }

posted @ 2019-06-27 08:03  明天喝可乐  阅读(286)  评论(0)    收藏  举报