博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java----线程协作的经典例子&生产者和消费者问题
阅读量:4660 次
发布时间:2019-06-09

本文共 7488 字,大约阅读时间需要 24 分钟。

 

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



 

 再贴上一些相关的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\t\tThread 2\t\tBalance");13         executor.execute(new DepositTask());//存钱任务14         executor.execute(new WithdrawTask());//取钱任务15         executor.shutdown();16     }17     18     public static class DepositTask implements Runnable {19         @Override20         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         @Override35         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("\t\t\tWait for a deposit.");57 newDeposit.await();//没钱就等着58 }59 //当余额不足时,等下一笔存款到账,取钱的任务等待。60 balance -= amount;61 System.out.println("\t\t\tWithdraw " + 62 amount + "\t\t" + 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 + "\t\t\t\t\t" + 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         @Override20         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         @Override36         public void run() {37             try{38                 while (true) {39                     System.out.println("\t\t\tConsumer 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 size50         private LinkedList
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("\t\t\tWait 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 }

 

转载于:https://www.cnblogs.com/zuosy/p/7442386.html

你可能感兴趣的文章
测试数据
查看>>
Hello world!
查看>>
【转贴】龙芯生态产品和解决方案巡展(第四篇)——存储
查看>>
JavaWeb过滤器
查看>>
2019牛客暑期多校训练营(第一场) B Integration (数学)
查看>>
POJ 2456 编程技巧之------二分查找思想的巧妙应用
查看>>
PostgreSQL使用总结
查看>>
Android开源的数据库框架
查看>>
ecjtu-summer training #3
查看>>
45-异常(2)
查看>>
ubuntu14.04环境下spyder的安装
查看>>
0x04基础套接字
查看>>
centos6.5_64bit_tomcat7开机自启
查看>>
prolog第一个程序
查看>>
Mutation test
查看>>
一个C++的轻量级的logger实现
查看>>
CodeForces 708B Recover the String
查看>>
CodeForces 666A Reberland Linguistics(DP)
查看>>
【题解】洛谷P2421[NOI2002]荒岛野人 (Exgcd)
查看>>
EXT学习笔记——几个小问题
查看>>