zoukankan      html  css  js  c++  java
  • 多线程之Java中的等待唤醒机制

      多线程的问题中的经典问题是生产者和消费者的问题,就是如何让线程有序的进行执行,获取CPU执行时间片的过程是随机的,如何能够让线程有序的进行,Java中提供了等待唤醒机制很好的解决了这个问题!

      生产者消费者经典的线程中的问题其实是解决线程中的通讯问题,就是不同种类的线程针对同一资源的操作,这里其实有一张图很好的阐述了这其中的问题:

      1 //代码中的实体类
      2 public class Student {
      3     String name;
      4     int age;
      5     boolean flag; // 默认情况是没有数据,如果是true,说明有数据
      6 }
      7 
      8 public class SetThread implements Runnable {
      9 
     10     private Student s;
     11     private int x = 0;
     12 
     13     public SetThread(Student s) {
     14         this.s = s;
     15     }
     16 
     17     @Override
     18     public void run() {
     19         while (true) {
     20             synchronized (s) {
     21                 //判断有没有
     22                 if(s.flag){
     23                     try {
     24                         s.wait(); //t1等着,释放锁
     25                     } catch (InterruptedException e) {
     26                         e.printStackTrace();
     27                     }
     28                 }
     29                 
     30                 if (x % 2 == 0) {
     31                     s.name = "林青霞";
     32                     s.age = 27;
     33                 } else {
     34                     s.name = "刘意";
     35                     s.age = 30;
     36                 }
     37                 x++; //x=1
     38                 
     39                 //修改标记
     40                 s.flag = true;
     41                 //唤醒线程
     42                 s.notify(); //唤醒t2,唤醒并不表示你立马可以执行,必须还得抢CPU的执行权。
     43             }
     44             //t1有,或者t2有
     45         }
     46     }
     47 }
     48 
     49 public class GetThread implements Runnable {
     50     private Student s;
     51 
     52     public GetThread(Student s) {
     53         this.s = s;
     54     }
     55 
     56     @Override
     57     public void run() {
     58         while (true) {
     59             synchronized (s) {
     60                 if(!s.flag){
     61                     try {
     62                         s.wait(); //t2就等待了。立即释放锁。将来醒过来的时候,是从这里醒过来的时候
     63                     } catch (InterruptedException e) {
     64                         e.printStackTrace();
     65                     }
     66                 }
     67                 
     68                 System.out.println(s.name + "---" + s.age);
     69                 //林青霞---27
     70                 //刘意---30
     71                 
     72                 //修改标记
     73                 s.flag = false;
     74                 //唤醒线程
     75                 s.notify(); //唤醒t1
     76             }
     77         }
     78     }
     79 }
     80 
     81 /*
     82  * 分析:
     83  *         资源类:Student    
     84  *         设置学生数据:SetThread(生产者)
     85  *         获取学生数据:GetThread(消费者)
     86  *         测试类:StudentDemo
     87  * 
     88  * 问题1:按照思路写代码,发现数据每次都是:null---0
     89  * 原因:我们在每个线程中都创建了新的资源,而我们要求的时候设置和获取线程的资源应该是同一个
     90  * 如何实现呢?
     91  *         在外界把这个数据创建出来,通过构造方法传递给其他的类。
     92  * 
     93  * 问题2:为了数据的效果好一些,我加入了循环和判断,给出不同的值,这个时候产生了新的问题
     94  *         A:同一个数据出现多次
     95  *         B:姓名和年龄不匹配
     96  * 原因:
     97  *         A:同一个数据出现多次
     98  *             CPU的一点点时间片的执行权,就足够你执行很多次。
     99  *         B:姓名和年龄不匹配
    100  *             线程运行的随机性
    101  * 线程安全问题:
    102  *         A:是否是多线程环境        是
    103  *         B:是否有共享数据        是
    104  *         C:是否有多条语句操作共享数据    是
    105  * 解决方案:
    106  *         加锁。
    107  *         注意:
    108  *             A:不同种类的线程都要加锁。
    109  *             B:不同种类的线程加的锁必须是同一把。
    110  * 
    111  * 问题3:虽然数据安全了,但是呢,一次一大片不好看,我就想依次的一次一个输出。
    112  * 如何实现呢?
    113  *         通过Java提供的等待唤醒机制解决。
    114  * 
    115  * 等待唤醒:
    116  *         Object类中提供了三个方法:
    117  *             wait():等待
    118  *             notify():唤醒单个线程
    119  *             notifyAll():唤醒所有线程
    120  *         为什么这些方法不定义在Thread类中呢?
    121  *             这些方法的调用必须通过锁对象调用,而我们刚才使用的锁对象是任意锁对象。
    122  *             所以,这些方法必须定义在Object类中。
    123  */
    124 public class StudentDemo {
    125     public static void main(String[] args) {
    126         //创建资源
    127         Student s = new Student();
    128         
    129         //设置和获取的类
    130         SetThread st = new SetThread(s);
    131         GetThread gt = new GetThread(s);
    132 
    133         //线程类
    134         Thread t1 = new Thread(st);
    135         Thread t2 = new Thread(gt);
    136 
    137         //启动线程
    138         t1.start();
    139         t2.start();
    140     }
    141 }

    线程的状态转换图及常见执行情况:

     

    上述代码的优化方案:

     1 //创建对象的时候实现线程安全
     2 public class Student {
     3     private String name;
     4     private int age;
     5     private boolean flag; // 默认情况是没有数据,如果是true,说明有数据
     6 
     7     public synchronized void set(String name, int age) {
     8         // 如果有数据,就等待
     9         if (this.flag) {
    10             try {
    11                 this.wait();
    12             } catch (InterruptedException e) {
    13                 e.printStackTrace();
    14             }
    15         }
    16 
    17         // 设置数据
    18         this.name = name;
    19         this.age = age;
    20 
    21         // 修改标记
    22         this.flag = true;
    23         this.notify();
    24     }
    25 
    26     public synchronized void get() {
    27         // 如果没有数据,就等待
    28         if (!this.flag) {
    29             try {
    30                 this.wait();
    31             } catch (InterruptedException e) {
    32                 e.printStackTrace();
    33             }
    34         }
    35 
    36         // 获取数据
    37         System.out.println(this.name + "---" + this.age);
    38 
    39         // 修改标记
    40         this.flag = false;
    41         this.notify();
    42     }
    43 }
    44 
    45 public class GetThread implements Runnable {
    46     private Student s;
    47 
    48     public GetThread(Student s) {
    49         this.s = s;
    50     }
    51 
    52     @Override
    53     public void run() {
    54         while (true) {
    55             s.get();
    56         }
    57     }
    58 }
    59 
    60 public class SetThread implements Runnable {
    61 
    62     private Student s;
    63     private int x = 0;
    64 
    65     public SetThread(Student s) {
    66         this.s = s;
    67     }
    68 
    69     @Override
    70     public void run() {
    71         while (true) {
    72             if (x % 2 == 0) {
    73                 s.set("林青霞", 27);
    74             } else {
    75                 s.set("刘意", 30);
    76             }
    77             x++;
    78         }
    79     }
    80 }
    81 
    82 public class StudentDemo {
    83     public static void main(String[] args) {
    84         //创建资源
    85         Student s = new Student();
    86         
    87         //设置和获取的类
    88         SetThread st = new SetThread(s);
    89         GetThread gt = new GetThread(s);
    90 
    91         //线程类
    92         Thread t1 = new Thread(st);
    93         Thread t2 = new Thread(gt);
    94 
    95         //启动线程
    96         t1.start();
    97         t2.start();
    98     }
    99 }
  • 相关阅读:
    例3-7 统计字符
    大一第四周
    例3-5 四则运算
    MyBatis 查询缓存
    MyBatis 延迟加载
    Mybatis 关联查询
    Mybatis 动态SQL
    MyBatis mapper动态代理
    MyBatis 增删改查
    MyBatis 框架搭建
  • 原文地址:https://www.cnblogs.com/ssh-html/p/10111489.html
Copyright © 2011-2022 走看看