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 }
  • 相关阅读:
    Knockout应用开发指南 第八章:简单应用举例(2)
    微软ASP.NET站点部署指南(7):生产环境部署
    Knockout应用开发指南 第七章:Mapping插件
    《Microsoft Sql server 2008 Internals》读书笔记第九章Plan Caching and Recompilation(6)
    《Microsoft Sql server 2008 Internals》读书笔记第九章Plan Caching and Recompilation(5)
    《Microsoft Sql server 2008 Internals》读书笔记第九章Plan Caching and Recompilation(3)
    《Microsoft Sql server 2008 Internals》读书笔记第九章Plan Caching and Recompilation(9)
    《Microsoft Sql server 2008 Internals》读书笔记第九章Plan Caching and Recompilation(8)
    Microsoft Visual Studio .NET 2003 引导程序插件下载地址(非官方)
    Vs2010在没有安装SQL Server 2005/2008 Express时如何连接MDF数据文件?
  • 原文地址:https://www.cnblogs.com/ssh-html/p/10111489.html
Copyright © 2011-2022 走看看