Java 多线程编程之六:线程之间的通信(附源代码)

来源:互联网 发布:南京中兴新软件 编辑:程序博客网 时间:2024/06/03 00:06

Java 多线程编程之六:线程之间的通信(附源代码)

源代码下载

       多线程编程中,线程之间的通信是一个比较复杂的问题。大家往往搞不懂什么是竞争资源?什么时候考虑同步?怎么进行同步?什么是线程通信?怎么进行通信?很多朋友面试的时候都遇到过这样类似的一个编程题:给定一个场景,编写代码来恰当使用等待、通知和通知所有线程。相信很多朋友对java.lang.Object类的这三个方法都很熟悉,notify、notifyAll、wait,但是真正能运用自如的却不多。因此面试中挂在上面那个面试题上的朋友还真不在少数。本文列举了一个经典的生产者、消费者源代码,以代码的角度来说明这个问题,并附加示例源代码,相信读者看过之后对线程之间的通信交互会有更进一步的认识!
        生产者-消费者模型,堪称多线程程序中的经典。本源码中将使用 java.lang.Object 的 wait、notify、notifyAll 来实现这个模型,这才是最重要的。
        开始以前,让我们先来熟悉一下生产者-消费者模型的游戏规则:
1、仓满不能生产;
2、藏空不能消费;
3、消费者消费产品前发现不能满足此次消费后通知生产者进行生产;
4、生产者生产出产品后通知消费者进行消费。

        好的。开始之前再来回顾一下对象锁的概念……这是最关键的。每个对象都有一个内置锁。当程序运行到非静态 synchronized 方法上时,将自动获得与正在执行代码类的当前实例(即 this 实例)有关的锁。java.lang.Thread.sleep() 方法执行时并不释放此锁;java.lang.Object.wait() 方法执行时释放此锁。好了,就到这里吧,说太多了一来显得作者罗嗦,二来也有侮辱读者 Java 基础的嫌疑。开始代码演示。
生产者-消费者模型-仓库源代码

[java] view plaincopyprint?
  1. package com.defonds.thread;  
  2. /** 
  3.  *  
  4.  *  
  5.  * 项目名称:ThreadApp 
  6.  * 类名称:Godown 
  7.  * 类描述:生产者-消费者模型之仓库 
  8.  * 创建人:Defonds 
  9.  * 创建时间:2010-1-26 上午10:50:00 
  10.  * 修改人:Defonds 
  11.  * 修改时间:2010-1-26 上午10:50:00 
  12.  * 修改备注: 
  13.  * @version  
  14.  * 
  15.  */  
  16. public class Godown {  
  17.       
  18.     private final int max_size = 100;//最大库存容量  
  19.     private int curNum;//现有库存量  
  20.       
  21.     /** 
  22.      *  
  23.      * 创建一个新的实例 Godown. 
  24.      * 
  25.      * @param curNum 
  26.      */  
  27.     public Godown(int curNum){  
  28.         this.curNum = curNum;  
  29.     }  
  30.       
  31.     /** 
  32.      * 生产指定数目的产品 
  33.      */  
  34.     public synchronized void produce(int needNum){  
  35.         while(true){  
  36.             /** 
  37.              * 如果不需要生产,进入等待状态 
  38.              */  
  39.             while(this.curNum + needNum > this.max_size){  
  40.                 System.out.println(Thread.currentThread().getName() + "要生产的产品数量" + needNum + "已经超出剩余库存容量" + (this.max_size - this.curNum) +",暂时不能进行生产任务!");  
  41.                 try {  
  42.                     this.wait();  
  43.                 } catch (InterruptedException e) {  
  44.                     // TODO Auto-generated catch block  
  45.                     e.printStackTrace();  
  46.                 }  
  47.             }  
  48.               
  49.             /** 
  50.              * 满足了生产条件,进行产品生产 
  51.              */  
  52.             this.curNum += needNum;  
  53.             System.out.println(Thread.currentThread().getName() + "已经生产了" + needNum + ",现存库存量是为:" + this.curNum);  
  54.               
  55.             /** 
  56.              * 唤醒在此对象监视器上等待的所有线程 
  57.              */  
  58.             this.notifyAll();  
  59.         }  
  60.     }  
  61.       
  62.     /** 
  63.      * 消费指定数目的产品 
  64.      */  
  65.     public synchronized void consume(int needNum){  
  66.         while(true){  
  67.             /** 
  68.              * 如果不可以消费,进入等待状态 
  69.              */  
  70.             while(this.curNum < needNum){  
  71.                 System.out.println(Thread.currentThread().getName() + "要消费的产品数量" + needNum + "已经超出剩余库存量" + this.curNum + ",暂时不能进行消费!");  
  72.                 try {  
  73.                     this.wait();  
  74.                 } catch (InterruptedException e) {  
  75.                     // TODO Auto-generated catch block  
  76.                     e.printStackTrace();  
  77.                 }  
  78.             }  
  79.               
  80.             /** 
  81.              * 满足了消费条件,进行产品消费 
  82.              */  
  83.             this.curNum -= needNum;  
  84.             System.out.println(Thread.currentThread().getName() + "已经消费了" + needNum + ",现存库存量是为:" + this.curNum);  
  85.               
  86.             /** 
  87.              * 唤醒在此对象监视器上等待的所有线程 
  88.              */  
  89.             this.notifyAll();  
  90.         }  
  91.     }  
  92. }  

生产者-消费者模型-生产者源代码

[java] view plaincopyprint?
  1. package com.defonds.thread;  
  2. /** 
  3.  *  
  4.  *  
  5.  * 项目名称:ThreadApp 
  6.  * 类名称:Producer 
  7.  * 类描述:生产者-消费者模型之生产者 
  8.  * 创建人:Defonds 
  9.  * 创建时间:2010-1-26 上午10:45:30 
  10.  * 修改人:Defonds 
  11.  * 修改时间:2010-1-26 上午10:45:30 
  12.  * 修改备注: 
  13.  * @version  
  14.  * 
  15.  */  
  16. public class Producer extends Thread{  
  17.       
  18.     private int needNum;//每次要生产产品的数量  
  19.     private Godown godown;//仓库  
  20.       
  21.     /** 
  22.      *  
  23.      * 创建一个新的实例 Producer. 
  24.      * 
  25.      * @param needNum 
  26.      * @param godown 
  27.      */  
  28.     public Producer(int needNum,Godown godown){  
  29.         this.needNum = needNum;  
  30.         this.godown = godown;  
  31.     }  
  32.       
  33.     /** 
  34.      * 重写 java.lang.Thread 的 run 方法 
  35.      */  
  36.     public void run(){  
  37.         this.godown.produce(this.needNum);  
  38.     }  
  39. }  

生产者-消费者模型-消费者源代码

[java] view plaincopyprint?
  1. package com.defonds.thread;  
  2. /** 
  3.  *  
  4.  *  
  5.  * 项目名称:ThreadApp 
  6.  * 类名称:Consumer 
  7.  * 类描述:生产者-消费者模型之消费者 
  8.  * 创建人:Defonds 
  9.  * 创建时间:2010-1-26 上午10:50:48 
  10.  * 修改人:Defonds 
  11.  * 修改时间:2010-1-26 上午10:50:48 
  12.  * 修改备注: 
  13.  * @version  
  14.  * 
  15.  */  
  16. public class Consumer extends Thread{  
  17.       
  18.     private int needNum;//每次要消费产品的数量  
  19.     private Godown godown;//仓库  
  20.       
  21.     /** 
  22.      *  
  23.      * 创建一个新的实例 Consumer. 
  24.      * 
  25.      * @param needNum 
  26.      * @param godown 
  27.      */  
  28.     public Consumer(int needNum,Godown godown){  
  29.         this.needNum = needNum;  
  30.         this.godown = godown;  
  31.     }  
  32.       
  33.     /** 
  34.      * 重写 java.lang.Thread 的 run 方法 
  35.      */  
  36.     public void run(){  
  37.         this.godown.consume(this.needNum);  
  38.     }  
  39. }  

生产者-消费者模型-程序入口源代码

[java] view plaincopyprint?
  1. package com.defonds.thread;  
  2. /** 
  3.  *  
  4.  *  
  5.  * 项目名称:ThreadApp 
  6.  * 类名称:ThreadApp 
  7.  * 类描述:生产者-消费者模型之程序入口 
  8.  * 创建人:Defonds 
  9.  * 创建时间:2010-1-26 上午10:51:15 
  10.  * 修改人:Defonds 
  11.  * 修改时间:2010-1-26 上午10:51:15 
  12.  * 修改备注: 
  13.  * @version  
  14.  * 
  15.  */  
  16. public class ThreadApp {  
  17.     public static void main(String[] args) {  
  18.           
  19.         /** 
  20.          * 仓库初始化 
  21.          */  
  22.         Godown godown = new Godown(30);  
  23.           
  24.         /** 
  25.          * 消费者初始化 
  26.          */  
  27.         Consumer consumer0 = new Consumer(30,godown);  
  28.         Consumer consumer1 = new Consumer(20,godown);  
  29.         Consumer consumer2 = new Consumer(40,godown);  
  30.           
  31.         /** 
  32.          * 生产者初始化 
  33.          */  
  34.         Producer producer0 = new Producer(10,godown);  
  35.         Producer producer1 = new Producer(10,godown);  
  36.         Producer producer2 = new Producer(10,godown);  
  37.         Producer producer3 = new Producer(10,godown);  
  38.         Producer producer4 = new Producer(10,godown);  
  39.         Producer producer5 = new Producer(10,godown);  
  40.         Producer producer6 = new Producer(10,godown);  
  41.         Producer producer7 = new Producer(10,godown);  
  42.           
  43.         /** 
  44.          * 标记每个生产者/消费者 
  45.          */  
  46.         consumer0.setName("consumer0");  
  47.         consumer1.setName("consumer1");  
  48.         consumer2.setName("consumer2");  
  49.         producer0.setName("producer0");  
  50.         producer1.setName("producer1");  
  51.         producer2.setName("producer2");  
  52.         producer3.setName("producer3");  
  53.         producer4.setName("producer4");  
  54.         producer5.setName("producer5");  
  55.         producer6.setName("producer6");  
  56.         producer7.setName("producer7");  
  57.           
  58.         /** 
  59.          * 开始进行生产-消费 
  60.          */  
  61.         consumer0.start();  
  62.         consumer1.start();  
  63.         consumer2.start();  
  64.         producer0.start();  
  65.         producer1.start();  
  66.         producer2.start();  
  67.         producer3.start();  
  68.         producer4.start();  
  69.         producer5.start();  
  70.         producer6.start();  
  71.         producer7.start();  
  72.     }  
  73. }  

        一次执行本代码时的控制台输出片段:
consumer2已经消费了40,现存库存量是为:20
consumer2要消费的产品数量40已经超出剩余库存量20,暂时不能进行消费!
consumer0要消费的产品数量30已经超出剩余库存量20,暂时不能进行消费!
consumer1已经消费了20,现存库存量是为:0
consumer1要消费的产品数量20已经超出剩余库存量0,暂时不能进行消费!
consumer0要消费的产品数量30已经超出剩余库存量0,暂时不能进行消费!
consumer2要消费的产品数量40已经超出剩余库存量0,暂时不能进行消费!
producer5已经生产了10,现存库存量是为:10
producer5已经生产了10,现存库存量是为:20
producer5已经生产了10,现存库存量是为:30
producer5已经生产了10,现存库存量是为:40
producer5已经生产了10,现存库存量是为:50
producer5已经生产了10,现存库存量是为:60
producer5已经生产了10,现存库存量是为:70
producer5已经生产了10,现存库存量是为:80
producer5已经生产了10,现存库存量是为:90
producer5已经生产了10,现存库存量是为:100
producer5要生产的产品数量10已经超出剩余库存容量0,暂时不能进行生产任务!
producer1要生产的产品数量10已经超出剩余库存容量0,暂时不能进行生产任务!
producer0要生产的产品数量10已经超出剩余库存容量0,暂时不能进行生产任务!
producer2要生产的产品数量10已经超出剩余库存容量0,暂时不能进行生产任务!
producer4要生产的产品数量10已经超出剩余库存容量0,暂时不能进行生产任务!
producer3要生产的产品数量10已经超出剩余库存容量0,暂时不能进行生产任务!
producer6要生产的产品数量10已经超出剩余库存容量0,暂时不能进行生产任务!
producer7要生产的产品数量10已经超出剩余库存容量0,暂时不能进行生产任务!
consumer2已经消费了40,现存库存量是为:60
consumer2已经消费了40,现存库存量是为:20
consumer2要消费的产品数量40已经超出剩余库存量20,暂时不能进行消费!
consumer0要消费的产品数量30已经超出剩余库存量20,暂时不能进行消费!
consumer1已经消费了20,现存库存量是为:0
consumer1要消费的产品数量20已经超出剩余库

        当然,本程序还存在一点问题,那就是一旦某个生产者得到 CPU 资源后,生产出一次产品后,尽管使用 notifyAll,但是却并没有让出 CPU 资源,以至于其他生产者无法再得到 CPU 资源;消费者也是如此。相信聪明的读者会找到更佳的办法来解决这个问题。但本例子主要是用来给大家演示线程之间的交互,就这一点需求来说,本例子足够了。

0 0
原创粉丝点击