Java多线程[4]:线程间通信

Java中线程间通信的方法

有这么几个方法,它们就定义在大家都非常熟悉的Object类中,但是大家却从来没有调用过,并且也不知道是做什么的,今天就由我带着你们熟悉一下下面的这三个方法。它们都是定义在Object类中的final方法,并且只能在synchronized上下文中调用。

  • wait()方法使当前线程进入休眠,直到另一个线程进入同一个监视器并调用nofity()方法。
  • nofity() 方法唤醒同一监视器内调用wait() 方法的线程,就是上面那位。
  • nofityAll()方法是nofify方法的升级版,会唤醒同一监视器内调用wait()方法的所有线程,但是由于这些线程都是同步线程,所以,只有一个线程将被授权访问。

生产者与消费者的场景

如果你现在还不明白,没关系,下面我会用一个“生产者与消费者”的场景来说明。首先,我会以不正确的方式来实现生产者与消费者。出现问题后,我再用wait()方法和notify() 方法来解决问题,相信大家一看就会明白。

关于生产者与消费者

生产者与消费者是一个经典的队列问题:生产者生产只有在所有产品都消费完了以后才进行生产,消费者只有在有产品的情况下才能消费,通俗来讲就是,生产一个,消费一个,再生产一个,再消费一个。。。。。。不能连续生产或连续消费。

不正确的方式

该例子包含4个类,Queue是队列,放置生产者生产的产品,Producer和Consumer分别是生产者类和消费者类,Program类是包含main方法的类。
首先是Queue类,该类包含get()和put()这两个同步方法

public class Queue {
    int n;

    synchronized int get() {
        System.out.println("Got:" + n);
        return n;
    }

    synchronized void put(int n) {
        this.n = n;
        System.out.println("Put:" + n);
    }
}

接下来分别是Producer类和Consumer类,这两个类都实现了Runnable接口并覆写了run()方法。

public class Producer implements Runnable {
    Queue q;

    public Producer(Queue q) {
        this.q = q;
        new Thread(this, "Producer").start();
    }

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            q.put(i);
        }
    }
}
public class Consumer implements Runnable {
    Queue q;

    public Consumer(Queue q) {
        this.q = q;
        new Thread(this, "Consumer").start();
    }

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            q.get();
        }
    }
}

下面是main()方法

public class Program {

    public static void main(String[] args) {
        Queue q = new Queue();
        new Producer(q);
        new Consumer(q);
    }
}

尽管Queue类中的get()和put()方法都是同步的,但是并不能阻止生产者连续生产,也不能阻止消费者连续消费,这就导致输出的结果是错误的

Put:0
Put:1
Put:2
Put:3
Got:3
Got:3
Got:3
Got:3
Got:3
Put:4

使用wait()方法和notify()方法改造队列

接下来我们使用wait()方法和notify()方法来改造Queue类,代码如下

public class Queue {
    int n;
    boolean valueSet = false;

    synchronized int get() {
        while (!valueSet) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("Got:" + n);
        valueSet = false;
        notify();
        return n;
    }

    synchronized void put(int n) {
        while (valueSet) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.n = n;
        valueSet = true;
        System.out.println("Put:" + n);
        notify();
    }
}

改造后的队列的get()方法能够防止连续消费,put()方法能够防止连续生产,运行程序后的输出如下:

Put:0
Got:0
Put:1
Got:1
Put:2
Got:2
Put:3
Got:3
Put:4
Got:4

大家可以好好体会一下wait()和notify()方法,以及生产者和消费者

你可能感兴趣的:(java,se)