zoukankan      html  css  js  c++  java
  • Java----线程协作的经典例子&生产者和消费者问题

    讲一瞎背景故事:创建并启动两个任务,一个用来向账户中存款,另一个从同一账户中提款。当提款的数额大于账户的当前余额时,提款线程必须等待。不管什么时候,只要向账户新存入一笔资金,存储线程必须通知提款线程重新常识。如果余额扔未达到提款的数额,提款线程必须继续等待新的存款。



     再贴上一些相关的API图片

    对于Condition的了解,我也不是很深。我觉得就是进程之间通信的条件吧。

    再加上关于Java显式锁的API吧。

    为了良心上过得去,加上这个。

    这个是缀重要的。


    代码加上注释了。我就不冗长的解释了。

     1 package thread;
     2 
     3 import java.util.concurrent.*;
     4 import java.util.concurrent.locks.*;
     5 
     6 public class ThreadCooperation {
     7     private static Account account = new Account();
     8     
     9     public static void main(String[] args) {
    10         ExecutorService executor = Executors.newFixedThreadPool(2);
    11         
    12         System.out.println("Thread 1		Thread 2		Balance");
    13         executor.execute(new DepositTask());//存钱任务
    14         executor.execute(new WithdrawTask());//取钱任务
    15         executor.shutdown();
    16     }
    17     
    18     public static class DepositTask implements Runnable {
    19         @Override
    20         public void run() {
    21             try{
    22                 while(true) {
    23                     account.deposit((int)(Math.random() * 10) + 1);
    24                     //如果不加这个休眠的话,电脑的运算速度会教你做人的。
    25                     Thread.sleep(1000);
    26                 }
    27             } catch(Exception ex) {
    28                 ex.printStackTrace();
    29             }
    30         }
    31     }
    32     
    33     public static class WithdrawTask implements Runnable {
    34         @Override
    35         public void run() {
    36             while(true) {
    37                 account.withdraw((int)(Math.random() * 10) + 1);//疯狂取钱
    38             }
    39         }
    40     }
    41     
    42     private static class Account {
    43         private static Lock lock = new ReentrantLock();//锁 没毛病
    44         private static Condition newDeposit = lock.newCondition();
    45         private int balance = 0;//余额
    46         
    47         public int getBalance() {
    48             return balance;
    49         }
    50         
    51         public void withdraw(int amount) {//取钱
    52             lock.lock();
    53             //良好的编程习惯 lock之后 try...catch...finally里面解锁
    54             try{
    55                 while(balance < amount) {
    56                     System.out.println("			Wait for a deposit.");
    57                     newDeposit.await();//没钱就等着
    58                 }
    59                 //当余额不足时,等下一笔存款到账,取钱的任务等待。
    60                 balance -= amount;
    61                 System.out.println("			Withdraw " + 
    62                         amount + "		" + getBalance());
    63                 
    64             } catch (InterruptedException ex) {
    65                 ex.printStackTrace();
    66             } finally {
    67                 lock.unlock();
    68             }
    69         }
    70         
    71         public void deposit(int amount) {//存钱
    72             lock.lock();
    73             try{
    74                 balance += amount;
    75                 System.out.println("Deposit " + 
    76                         amount + "					" + getBalance());
    77                 
    78                 //newDeposit.signalAll();
    79                 newDeposit.signal();
    80             } catch (IllegalMonitorStateException e) {
    81                 e.printStackTrace();
    82             } finally {
    83                 lock.unlock();
    84             }
    85         }
    86     }
    87 }

     


    补充:

    看了书后面又有一个问题,问的是   55~57行代码

    while 关键字 换成if可不可以???

    运行了一下,发现是不可以的。

    这是为什么呢???

    当存入一笔money之后,就会执行这里之后的代码。然而,我们判断balance<amount 用的是if 所以不会判断你存入money 之后 余额和要取钱的关系。所以就会透支了。

    简单的说,我要取100块钱,account里面的balance只有一毛钱。所以只能等待,等下一笔钱存入。当下一笔钱存入之后,我没有判断,现在account里面的balance。(万一我又存了一毛呢???)。 就执行了balance -= amount; 所以透支了。


    生产者 / 消费者

    继续瞎讲:假设使用缓冲区存储整数。缓冲区的大小是受限的。缓冲区提供write(int) 方法将一个int 值添加到缓冲区中,还提供方法read() 从缓冲区中读取和删除一个int值。为了同步这个操作,使用具有两个条件的锁,notEmpty(缓冲区非空) 和 notFul(缓冲区未满)。

    当任务向缓冲区添加一个int时, 如果缓冲区是满的,那么任务将会等待notFull条件。当任务从缓冲区中读取一个int时,如果缓冲区是空的,那么任务将等待notEmpty条件。

    ——《Java语言程序设计 进阶篇》


    我的分析:我觉的书上是有点逆向思维的意思。我的理解notEmpty是对于消费者来说的, 生产者把int 放到buffer里面 就会告诉消费者notEmpty.singal();        当消费者要去一个空的buffer取值时,notEmpty就是false 就是一个假命题,所以要告诉消费者等待(notEmpty.await())。

    同理,我再叨叨一遍生产者的notFull。生产者嘛,就是不停地生产int 放到buffer里面。生产者是个好同志,他每天忙于生产。怎样才能让他休息呢?很简单,buffer满了生产者就没有办法生产了(再生产就没地方放了,生产个锤子)。这个时候notFill.await()就让生产者休息。       那什么时候再工作呢?只要buffer有空间就生产。当消费者取走了一个int时,消费者不是小偷,取走了一个int要跟生产这说一声:“Buffer is notFull(notFull.signal()).” 这时候勤劳的生产者同志就开始生产了。

    配合着上面的图,我想应该能理解的更充分。这样代码应该能理解了。

     1 package thread;
     2 
     3 import java.util.LinkedList;
     4 import java.util.concurrent.*;
     5 import java.util.concurrent.locks.*;
     6 
     7 public class ConsumerProducer {
     8     private static Buffer buffer = new Buffer();//缓冲
     9     
    10     public static void main(String[] args) {
    11         ExecutorService executor = 
    12                 Executors.newFixedThreadPool(2);//两个线程
    13         executor.execute(new ProducerTask());//生产者
    14         executor.execute(new ConsumerTask());//消费者
    15         executor.shutdown();
    16     }
    17     
    18     private static class ProducerTask implements Runnable {
    19         @Override
    20         public void run() {
    21             try{
    22                 int i = 1;
    23                 while(true) {
    24                     System.out.println("Producer writes " + i);
    25                     buffer.write(i++);
    26                     Thread.sleep((int)(Math.random() * 4500));
    27                 }
    28             } catch(InterruptedException ex) {
    29                 ex.printStackTrace();
    30             }
    31         }
    32     }
    33     
    34     private static class ConsumerTask implements Runnable {
    35         @Override
    36         public void run() {
    37             try{
    38                 while (true) {
    39                     System.out.println("			Consumer reads " + buffer.read());
    40                     Thread.sleep((int)(Math.random() * 5000));
    41                 }
    42             } catch(InterruptedException ex) {
    43                 ex.printStackTrace();
    44             }
    45         }
    46     }
    47     
    48     private static class Buffer {
    49         private static final int CAPACITY = 1;//buffer size
    50         private LinkedList<Integer> queue =
    51                 new LinkedList<>();
    52         
    53         private static Lock lock = new ReentrantLock();
    54         
    55         private static Condition notEmpty = lock.newCondition();//告诉消费者buffer不空
    56         private static Condition notFull = lock.newCondition(); //告诉生产者buffer不是满的
    57         
    58         public void write(int value) {//生产者写
    59             lock.lock();
    60             try {
    61                 while(queue.size() == CAPACITY) {
    62                     System.out.println("Wait for notFull condition");
    63                     notFull.await();//buffer满了 就等一等 等到notFull再生产
    64                 }
    65                 
    66                 queue.offer(value);
    67                 System.out.println(value + " in");//这个可以注释 测试用的 很有用
    68                 notEmpty.signal();//既然生产了,就可以告诉消费者 消费了。
    69             } catch(InterruptedException ex){
    70                 
    71             } finally {
    72                 lock.unlock();
    73             }
    74         }
    75         
    76         public int read() {//消费者读
    77             int value = 0;
    78             lock.lock();
    79             try {
    80                 while (queue.isEmpty()) {
    81                     System.out.println("			Wait for notEmpty condition");
    82                     notEmpty.await(); //空了 就告诉自己等一下,别吃了。
    83                 }
    84                 value = queue.remove();
    85                 System.out.println(value + " out");//这个可以注释 测试用的 很有用
    86                 notFull.signal();//消费了一个 告诉生产者 可以生产了。
    87             } catch (InterruptedException ex) {
    88                 ex.printStackTrace();
    89             } finally {
    90                 lock.unlock();
    91             }
    92             return value;
    93         }
    94     }
    95 }
  • 相关阅读:
    结巴分词 0.14 版发布,Python 中文分词库
    Lazarus 1.0.2 发布,Pascal 集成开发环境
    Android全屏 去除标题栏和状态栏
    服务器日志现 Android 4.2 传将添多项新特性
    Percona XtraBackup 2.0.3 发布
    长平狐 Android 强制设置横屏或竖屏 设置全屏
    NetBeans 7.3 Beta 发布,全新的 HTML5 支持
    CppDepend现在已经支持Linux
    GromJS 1.7.18 发布,服务器端的 JavaScript
    Apache OpenWebBeans 1.1.6 发布
  • 原文地址:https://www.cnblogs.com/zuosy/p/7442386.html
Copyright © 2011-2022 走看看