线程间的通信

扶醉桌前 提交于 2020-03-01 16:35:53

一,概述。 

  1.什么叫做线程间通信: 在1个进程中,线程往往不是孤立存在的,线程之间需要一些协调通信,来共同完成一件任务。也就是通过一定的方法来实现线程间的“交流”。

  2.线程间通信的体现:
    - 1个线程传递数据给另1个线程
    - 在1个线程中执行完特定任务后,转到另1个线程继续执行任务
二,线程通信的方法。
  Object类中相关的方法有两个notify方法和三个wait方法:notify() / notifyAll() / wait()

  因为wait和notify方法定义在Object类中,因此会被所有的类所继承。这些方法都是final的,即它们都是不能被重写的,不能通过子类覆写去改变它们的行为。

  1.wait()方法。

  语法:锁对象.wait()

  特点:wait()方法的调用使得当前线程必须要等待,直到另外一个线程调用notify()或者notifyAll()方法。

        wait()方法的调用必须在同步的前提下。(因为该方法是要用锁对象调用,而只有在同步的情况下才有锁)

        wait()方法的调用会导致锁的释放。

  线程调用wait()方法,释放它对锁的拥有权,然后等待另外的线程来通知它(通知的方式是notify()或者notifyAll()方法),这样它才能重新获得锁的拥有权和恢复执行。要确保调用wait()方法的时候拥有锁,即,wait()方法的调用必须放在synchronized方法或synchronized块中。

  一个小比较:

  当线程调用了wait()方法时,它会释放掉对象的锁。

  另一个会导致线程暂停的方法:Thread.sleep(),它会导致线程睡眠指定的毫秒数,但线程在睡眠的过程中是不会释放掉对象的锁的

 

  2.notify和notifyAll()
  语法:锁对象.notify()/notifyAll()
  特点:notify()方法的调用可以唤醒当前锁对象下等待的另一个单个线程。优先级较高的优先唤醒,如果优先级一样,则随机唤醒。notifyAll()方法的调用可以唤醒当前锁对象下等待的所有线程。
     notify()和notifyAll()方法必须在同步的前提下使用(也就是notify方法调用必须放在synchronized方法或synchronized块中)。
     被唤醒的线程是不能被执行的,需要等到当前线程放弃这个对象的锁,也就是想要唤醒其他线程,说光调用notify()不行,还必须要调用wait()方法让当前线程释放锁对象,当另外一个线程获得了锁对象即可以执行。
  注意:wait() / notify() / notifyAll()方法使用的锁对象必须和同步代码块中的锁对象是同一个,否则报异常!!!
 
 【代码演示】:线程间通信。
 1 class Bank1 {//公共资源
 2     int total=100;
 3 }
 4 class PresonA implements Runnable{
 5     Bank1  b;
 6     //因为要确保两个用户使用的是同一家银行,所以在主函数中new一个银行,然后通过构造函数将这一银行传递给这两个用户
 7     public PresonA(Bank1 b){
 8         this.b=b;//将银行的引用传给两个用户
 9     }
10     @Override
11     public void run() {
12         while (true){
13             synchronized (b){//同步代码块,因为要确保两个用户使用的是同一个锁,所以选择银行对象作为锁
14                 if(b.total>=0){//打钱,当卡里钱大于0,就唤醒另一个线程来取钱
15                     b.notify();//唤醒另一个线程来取钱,注意该方法是要用锁对象调用的
16                     try {
17                         b.wait();//接着将自己睡眠,然后把锁释放给两一个线程
18                     } catch (InterruptedException e) {
19                         e.printStackTrace();
20                     }
21                 }
22                 b.total=b.total+100;//A先存了100
23                 System.out.println("A存了100,目前存款为:"+b.total);
24             }
25         }
26     }
27 }
28 class PresonB implements Runnable{
29     Bank1  b;
30     public PresonB(Bank1 b){
31         this.b=b;
32     }
33     @Override
34     public void run() {
35         while (true){
36             synchronized (b){
37                 if(b.total<=0){//取钱,当卡里钱小于0,就唤醒另一个线程来存钱
38                     b.notify();//唤醒另一个线程
39                     try {
40                         b.wait();//将自己睡眠
41                     } catch (InterruptedException e) {
42                         e.printStackTrace();
43                     }
44                 }
45                 b.total=b.total-100;
46                 System.out.println("****B取了100,目前存款为:"+b.total);
47             }
48         }
49     }
50 }
51 public class CommunicationDemo {
52     public static void main(String[] args) {
53         Bank1 b=new Bank1();//作为公共资源传递给两个用户
54         new Thread(new PresonA(b)).start();
55         new Thread(new PresonB(b)).start();
56     }
57 }

 运行结果:

 

为什么两个线程的时候执行结果正确而四个线程的时候就不对了呢?

  因为线程在wait()的时候,接收到其他线程的通知,即往下执行,不再进行判断。

  两个线程的情况下,唤醒的肯定是另一个线程;但是在多个线程的情况下,就是随机唤醒,所以执行结果就会混乱无序。

  比如,一个可能的情况是,一个增加线程执行的时候,其他两个线程都在wait,这时候第一个线程调用了notify()方法,其他线程中的一个将被唤醒,然后执行各自的增加或减少方法。

解决的方法就是:就是让各个新城在被唤醒之后仍然进行条件判断

 

 
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!