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 }
原文地址:https://www.cnblogs.com/zuosy/p/7442386.html