zoukankan      html  css  js  c++  java
  • Android(java)学习笔记11:生产者和消费者之等待唤醒机制

    1. 首先我们根据梳理我们之前Android(java)学习笔记70中,关于生产者和消费者程序思路:

     2. 下面我们就要重点介绍这个等待唤醒机制

    (1)第一步:还是先通过代码体现出等待唤醒机制

    下面是测试类:

    package cn.itcast_05;
    
    /*
     * 分析:
     *         资源类:Student    
     *         设置学生数据:SetThread(生产者)
     *         获取学生数据:GetThread(消费者)
     *         测试类:StudentDemo
     * 
     * 问题1:按照思路写代码,发现数据每次都是:null---0
     * 原因:我们在每个线程中都创建了新的资源,而我们要求的时候设置和获取线程的资源应该是同一个
     * 如何实现呢?
     *         在外界把这个数据创建出来,通过构造方法传递给其他的类。
     * 
     * 问题2:为了数据的效果好一些,我加入了循环和判断,给出不同的值,这个时候产生了新的问题
     *         A:同一个数据出现多次
     *         B:姓名和年龄不匹配
     * 原因:
     *         A:同一个数据出现多次
     *             CPU的一点点时间片的执行权,就足够你执行很多次。
     *         B:姓名和年龄不匹配
     *             线程运行的随机性
     * 线程安全问题:
     *         A:是否是多线程环境        是
     *         B:是否有共享数据        是
     *         C:是否有多条语句操作共享数据    是
     * 解决方案:
     *         加锁。
     *         注意:
     *             A:不同种类的线程都要加锁。
     *             B:不同种类的线程加的锁必须是同一把。
     * 
     * 问题3:虽然数据安全了,但是呢,一次一大片不好看,我就想依次的一次一个输出。
     * 如何实现呢?
     *         通过Java提供的等待唤醒机制解决。
     * 
     * 等待唤醒:
     *         Object类中提供了三个方法:
     *             wait():等待
     *             notify():唤醒单个线程
     *             notifyAll():唤醒所有线程
     *         为什么这些方法不定义在Thread类中呢?
     *             这些方法的调用必须通过锁对象调用,而我们刚才使用的锁对象是任意锁对象。
     *             所以,这些方法必须定义在Object类中。
     */
    public class StudentDemo {
        public static void main(String[] args) {
            //创建资源
            Student s = new Student();
            
            //设置和获取的类
            SetThread st = new SetThread(s);
            GetThread gt = new GetThread(s);
    
            //线程类
            Thread t1 = new Thread(st);
            Thread t2 = new Thread(gt);
    
            //启动线程
            t1.start();
            t2.start();
        }
    }

     下面是生产者线程类:

     1 package cn.itcast_05;
     2 
     3 public class SetThread implements Runnable {
     4 
     5     private Student s;
     6     private int x = 0;
     7 
     8     public SetThread(Student s) {
     9         this.s = s;
    10     }
    11 
    12     @Override
    13     public void run() {
    14         while (true) {
    15             synchronized (s) {
    16                 //判断有没有
    17                 if(s.flag){
    18                     try {
    19                         s.wait(); //t1等着,释放锁
    20                     } catch (InterruptedException e) {
    21                         e.printStackTrace();
    22                     }
    23                 }
    24                 
    25                 if (x % 2 == 0) {
    26                     s.name = "林青霞";
    27                     s.age = 27;
    28                 } else {
    29                     s.name = "刘意";
    30                     s.age = 30;
    31                 }
    32                 x++; //x=1
    33                 
    34                 //修改标记
    35                 s.flag = true;
    36                 //唤醒线程
    37                 s.notify(); //唤醒t2,唤醒并不表示你立马可以执行,必须还得抢CPU的执行权。
    38             }
    39             //t1有,或者t2有
    40         }
    41     }
    42 }

     下面是消费者线程类:

     1 package cn.itcast_05;
     2 
     3 public class GetThread implements Runnable {
     4     private Student s;
     5 
     6     public GetThread(Student s) {
     7         this.s = s;
     8     }
     9 
    10     @Override
    11     public void run() {
    12         while (true) {
    13             synchronized (s) {
    14                 if(!s.flag){
    15                     try {
    16                         s.wait(); //t2就等待了。立即释放锁。将来醒过来的时候,是从这里醒过来的时候
    17                     } catch (InterruptedException e) {
    18                         e.printStackTrace();
    19                     }
    20                 }
    21                 
    22                 System.out.println(s.name + "---" + s.age);
    23                 //林青霞---27
    24                 //刘意---30
    25                 
    26                 //修改标记
    27                 s.flag = false;
    28                 //唤醒线程
    29                 s.notify(); //唤醒t1
    30             }
    31         }
    32     }
    33 }

     Student类:

    1 package cn.itcast_05;
    2 
    3 public class Student {
    4     String name;
    5     int age;
    6     boolean flag; // 默认情况是没有数据,默认是false,如果是true,说明有数据
    7 }

     

    (2)接下来我们对唤醒机制代码进行优化

    下面是测试类:

     1 package cn.itcast_07;
     2 
     3 /*
     4  * 分析:
     5  *         资源类:Student    
     6  *         设置学生数据:SetThread(生产者)
     7  *         获取学生数据:GetThread(消费者)
     8  *         测试类:StudentDemo
     9  * 
    10  * 问题1:按照思路写代码,发现数据每次都是:null---0
    11  * 原因:我们在每个线程中都创建了新的资源,而我们要求的时候设置和获取线程的资源应该是同一个
    12  * 如何实现呢?
    13  *         在外界把这个数据创建出来,通过构造方法传递给其他的类。
    14  * 
    15  * 问题2:为了数据的效果好一些,我加入了循环和判断,给出不同的值,这个时候产生了新的问题
    16  *         A:同一个数据出现多次
    17  *         B:姓名和年龄不匹配
    18  * 原因:
    19  *         A:同一个数据出现多次
    20  *             CPU的一点点时间片的执行权,就足够你执行很多次。
    21  *         B:姓名和年龄不匹配
    22  *             线程运行的随机性
    23  * 线程安全问题:
    24  *         A:是否是多线程环境        是
    25  *         B:是否有共享数据        是
    26  *         C:是否有多条语句操作共享数据    是
    27  * 解决方案:
    28  *         加锁。
    29  *         注意:
    30  *             A:不同种类的线程都要加锁。
    31  *             B:不同种类的线程加的锁必须是同一把。
    32  * 
    33  * 问题3:虽然数据安全了,但是呢,一次一大片不好看,我就想依次的一次一个输出。
    34  * 如何实现呢?
    35  *         通过Java提供的等待唤醒机制解决。
    36  * 
    37  * 等待唤醒:
    38  *         Object类中提供了三个方法:
    39  *             wait():等待
    40  *             notify():唤醒单个线程
    41  *             notifyAll():唤醒所有线程
    42  *         为什么这些方法不定义在Thread类中呢?
    43  *             这些方法的调用必须通过锁对象调用,而我们刚才使用的锁对象是任意锁对象。
    44  *             所以,这些方法必须定义在Object类中。
    45  * 
    46  * 最终版代码中47  *         把Student的成员变量给私有的了。
    48  *         把设置和获取的操作给封装成了功能,并加了同步。
    49  *         设置或者获取的线程里面只需要调用方法即可。
    50  */
    51 public class StudentDemo {
    52     public static void main(String[] args) {
    53         //创建资源
    54         Student s = new Student();
    55         
    56         //设置和获取的类
    57         SetThread st = new SetThread(s);
    58         GetThread gt = new GetThread(s);
    59 
    60         //线程类
    61         Thread t1 = new Thread(st);
    62         Thread t2 = new Thread(gt);
    63 
    64         //启动线程
    65         t1.start();
    66         t2.start();
    67     }
    68 }

     生产者线程类:

     1 package cn.itcast_07;
     2 
     3 public class SetThread implements Runnable {
     4 
     5     private Student s;
     6     private int x = 0;
     7 
     8     public SetThread(Student s) {
     9         this.s = s;
    10     }
    11 
    12     @Override
    13     public void run() {
    14         while (true) {
    15             if (x % 2 == 0) {
    16                 s.set("林青霞", 27);
    17             } else {
    18                 s.set("刘意", 30);
    19             }
    20             x++;
    21         }
    22     }
    23 }

     消费者线程类:

     1 package cn.itcast_07;
     2 
     3 public class GetThread implements Runnable {
     4     private Student s;
     5 
     6     public GetThread(Student s) {
     7         this.s = s;
     8     }
     9 
    10     @Override
    11     public void run() {
    12         while (true) {
    13             s.get();
    14         }
    15     }
    16 }

     Student类,同时Student内部封装了Student的两个同步方法(生产Student  和  消费Student)

     1 package cn.itcast_07;
     2 
     3 public class Student {
     4     private String name;
     5     private int age;
     6     private boolean flag; // 默认情况是没有数据,如果是true,说明有数据
     7 
     8     public synchronized void set(String name, int age) {
     9         // 如果有数据,就等待
    10         if (this.flag) {
    11             try {
    12                 this.wait();
    13             } catch (InterruptedException e) {
    14                 e.printStackTrace();
    15             }
    16         }
    17 
    18         // 设置数据
    19         this.name = name;
    20         this.age = age;
    21 
    22         // 修改标记
    23         this.flag = true;
    24         this.notify();
    25     }
    26 
    27     public synchronized void get() {
    28         // 如果没有数据,就等待
    29         if (!this.flag) {
    30             try {
    31                 this.wait();
    32             } catch (InterruptedException e) {
    33                 e.printStackTrace();
    34             }
    35         }
    36 
    37         // 获取数据
    38         System.out.println(this.name + "---" + this.age);
    39 
    40         // 修改标记
    41         this.flag = false;
    42         this.notify();
    43     }
    44 }

     

    3. sleep和wait的区别有:

    (1)这两个方法来自不同的类,分别是Thread和Object
    (2)最主要是sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法。
    (3)wait,notify 和 notifyAll 只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用
       synchronized(x){
            x.notify()
           //或者wait()
       }

    (4)sleep必须捕获异常,而wait,notify和notifyAll不需要捕获异常

  • 相关阅读:
    Parallel Decision Tree
    基础知识整理
    方差分析——单因素方差分析
    方差分析(2)
    方差分析(1)
    统计编程的框架与R语言统计分析基础——摘(2)统计分析之线性回归
    统计编程的框架与R语言统计分析基础——摘(1)
    龙门镖局
    公开课可下载资源汇总
    tomcat jdk servlet websocket版本对应关系及websocket 1.1的实现
  • 原文地址:https://www.cnblogs.com/hebao0514/p/4509997.html
Copyright © 2011-2022 走看看