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 }
  • 相关阅读:
    oracle里的查询转换
    Oracle里的优化器
    转:oracle几组重要的常见视图-v$undostat,v$open_cursor,v$rowcache,v$session_longops,v$waitstat
    转:oracle几组重要的常见视图-v$segstat,v$segment_statistics,v$filestat,v$rollstat
    转:oracle几组重要的常见视图-v$latch,v$latch_children,v$lock,v$locked_object
    转:oracle常见重要视图-v$sql,v$sql_plan,v$sqltext,v$sqlarea,v$sql_plan_statistcs
    转:oracle几组重要的常见视图-v$process,v$session,v$session_wait,v$session_event
    第三方引擎应用场景分析--Tokudb,infobright
    mysql 常见参数
    Uep的静态下拉和动态下拉建立
  • 原文地址:https://www.cnblogs.com/zuosy/p/7442386.html
Copyright © 2011-2022 走看看