Java学习笔记之线程(五):线程的通讯

来源:互联网 发布:三层端口聚合命令 编辑:程序博客网 时间:2024/05/18 00:08

package com.demo;/* * 线程通讯:一个线程完成了自己的任务时,要通知另外一个线程去完成另外一个任务; *  * 生产者与消费者:生产者生产出产品之后要通知消费者来消费产品;消费者消费完产品之后要通知生产者继续生产产品; *  * 下面模拟生产者与消费者的程序,输出结果时,出现了线程安全问题,价格错乱。 * 分析:当生产者抢到CPU的执行权的时候,生产者开始生产产品;假设生产者当前生产的是香蕉,价格是2.0,然后生产者 * 继续生产苹果,当执行到p.name="苹果";的时候,只修改了产品的名称,还没来得及修改产品的价格,CPU的执行权 * 就被消费者抢走了,那么此时消费者输出的就是苹果的价格是2.0。 *  * 解决办法:同步代码块; */// 产品类class Product{String name;// 产品名字double price;// 产品价格}// 生产者类class Producer extends Thread{// 生产者维护了一个产品类Product p;// 为了使生产者和消费者维护同一个产品类public Producer(Product p) {this.p = p;}@Overridepublic void run() {int i = 0;while (true){// 不停地生产两种产品if (i%2 == 0){p.name = "苹果";try {Thread.sleep(1);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}p.price = 6.5;}else{p.name = "香蕉";p.price = 2.0;}i++;// 生产者每生产一个产品后输出一句话System.out.println("生产者生产了" + p.name + ", 价格" + p.price);}}}// 消费者类class Customer extends Thread{// 消费者也维护了一个产品类Product p;// 为了使生产者和消费者维护同一个产品类public Customer(Product p) {this.p = p;}@Overridepublic void run() {while (true){// 消费者每消费完一个产品就输出一句话System.out.println("消费者消费了" + p.name + ", 价格是" + p.price);}}}public class Demo7 {public static void main(String[] args) {// 创建产品类对象,传入生产者和消费者类中,保证生产者和消费者维护的是同一个产品类Product p = new Product();// 创建生产者对象Producer producer = new Producer(p);// 创建消费者对象Customer customer = new Customer(p);// 调用start()方法开启线程producer.start();customer.start();}}


/* * 同步代码块解决线程安全问题: */// 产品类class Product{String name;// 产品名字double price;// 产品价格}// 生产者类class Producer extends Thread{// 生产者维护了一个产品类Product p;// 为了使生产者和消费者维护同一个产品类public Producer(Product p) {this.p = p;}@Overridepublic void run() {int i = 0;while (true){// 不停地生产两种产品// 同步代码块,用产品类作为锁对象;// 当生产者进行产品生产的时候,产品是被锁住的,消费者不能够对该产品进行消费;// 必须等生产者将产品生产完成之后,产品才会解锁,此时消费者才可以对产品进行消费;synchronized (p) {if (i%2 == 0){p.name = "苹果";try {Thread.sleep(1);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}p.price = 6.5;}else{p.name = "香蕉";p.price = 2.0;}i++;// 生产者每生产一个产品后输出一句话System.out.println("生产者生产了" + p.name + ", 价格" + p.price);}}}}// 消费者类class Customer extends Thread{// 消费者也维护了一个产品类Product p;// 为了使生产者和消费者维护同一个产品类public Customer(Product p) {this.p = p;}@Overridepublic void run() {while (true){// 同步代码块:使用产品类作为锁对象,当消费者正在消费产品的时候,该产品也是被锁住的;// 所以生产者不可以在消费者正在消费产品的时候对象产品进行修改,只能等消费者消费完成// 之后,生产者才可以进行生产;synchronized (p) {// 消费者每消费完一个产品就输出一句话System.out.println("消费者消费了" + p.name + ", 价格是" + p.price);}}}}public class Demo7 {public static void main(String[] args) {// 创建产品类对象,传入生产者和消费者类中,保证生产者和消费者维护的是同一个产品类Product p = new Product();// 创建生产者对象Producer producer = new Producer(p);// 创建消费者对象Customer customer = new Customer(p);// 调用start()方法开启线程producer.start();customer.start();}}


package com.demo;/* * 虽然解决了线程安全问题,但是还有一个问题:生产者每次都会生产多个产品,或者消费者每次都会消费多个产品。 * 我们需要的现象是:生产者生产完一个产品以后,紧接着消费者就来消费这个产品;当消费者消费完产品以后, * 生产者才继续生产产品;要想达到这样的功能,就需要线程通讯来完成。 *  * 线程通讯两个重要的方法: * wait():线程等待;如果线程执行了wait()方法,那么该线程就会进入等待的状态;等待状态下的线程 * 必须被其他线程调用notify()方法才能唤醒; * notify():唤醒线程池中处于等待状态的一个线程; * notifyAll():唤醒线程池中处于等待状态的所有线程; *  * wait()方法与notify()方法注意事项: * 1、这两个方法必须要由锁对象调用; * 因为一个线程如果调用了wait()方法,那么该线程就会进入一个以锁对象为标识符的线程池中等待; * 而一个线程如果调用了notify()方法,那么就会唤醒以锁对象为标识符的线程池中的一个等待线程; * 如果这两个方法不由锁对象调用,那么就不会创建以锁对象为标识符的线程池,也就没法唤醒指定的线程; * 2、这两个方法不是属于Thread类的,而是属于Object的; * 因为锁对象可以是任意对象,如果这两个方法设计在Thread对象上,那么锁对象就不可以是任意对象了; * 3、这两个方法必须要在同步代码块或者同步函数中才能使用; * 因为只有在同步代码块或者同步函数中才会有锁对象的存在,而这两个方法只能由锁对象调用; */// 产品类class Product{String name;// 产品名字double price;// 产品价格boolean flag = false;// 标记产品是否被生产了。默认是没有被生产;true表示产品被生产出来了;}// 生产者类class Producer extends Thread{// 生产者维护了一个产品类Product p;// 为了使生产者和消费者维护同一个产品类public Producer(Product p) {this.p = p;}@Overridepublic void run() {int i = 0;while (true){// 不停地生产两种产品// 同步代码块,用产品类作为锁对象;// 当生产者进行产品生产的时候,产品是被锁住的,消费者不能够对该产品进行消费;// 必须等生产者将产品生产完成之后,产品才会解锁,此时消费者才可以对产品进行消费;synchronized (p) {if (p.flag == false){ // 如果产品没有被生产,那么生产者就进行生产;if (i%2 == 0){p.name = "苹果";p.price = 6.5;}else{p.name = "香蕉";p.price = 2.0;}i++;// 生产者每生产一个产品后输出一句话System.out.println("生产者生产了" + p.name + ", 价格" + p.price);// 产品被生产出来以后,要修改产品标记p.flag = true;// 生产者生产出产品以后要唤醒消费者进行消费p.notify();}else{ // 如果产品已经生产出来了,就等待消费者进行消费;try {p.wait();} catch (InterruptedException e) {e.printStackTrace();}}}}}}// 消费者类class Customer extends Thread{// 消费者也维护了一个产品类Product p;// 为了使生产者和消费者维护同一个产品类public Customer(Product p) {this.p = p;}@Overridepublic void run() {while (true){// 同步代码块:使用产品类作为锁对象,当消费者正在消费产品的时候,该产品也是被锁住的;// 所以生产者不可以在消费者正在消费产品的时候对象产品进行修改,只能等消费者消费完成// 之后,生产者才可以进行生产;synchronized (p) {if (p.flag == true){  // 如果产品已经被生产出来了,消费者就进行消费// 消费者每消费完一个产品就输出一句话System.out.println("消费者消费了" + p.name + ", 价格是" + p.price);// 消费者消费完产品以后,将产品状态标记为未生产;p.flag = false;// 消费者消费完产品以后,要通知生产者继续进行生产p.notify();}else{// 如果产品没有被生产出来,消费者就等待;try {p.wait();} catch (InterruptedException e) {e.printStackTrace();}}}}}}public class Demo7 {public static void main(String[] args) {// 创建产品类对象,传入生产者和消费者类中,保证生产者和消费者维护的是同一个产品类Product p = new Product();// 创建生产者对象Producer producer = new Producer(p);// 创建消费者对象Customer customer = new Customer(p);// 调用start()方法开启线程producer.start();customer.start();}}