Java线程间通信

    |     2015年4月15日   |   多线程编程   |     0 条评论   |    1613

同步方法能挡住其他线程同时进同一个对象,但还不够精巧。生产者一边造数据、消费者一边用,还要互相等对方干完——如果靠轮询死循环盯条件,会浪费大量 CPU。

Java 用 wait()、notify()、notifyAll() 做线程间通信。它们是 Object 的 final 方法,必须在 synchronized 方法(或块)里调用。

方法 作用
wait() 放弃管程进入睡眠,直到同对象上有人 notify;另有可限时的重载
notify() 唤醒同一对象上某个正在 wait 的线程
notifyAll() 唤醒全部等待线程,优先级高的先跑

一、错误写法:同步了仍会超车

四个类:Q 是队列,Producer / Consumer 是线程,PC 把它们串起来。put / get 虽然 synchronized,但没有“有没有新数据”的约定,生产者可以连写,消费者可以连读同一个值。

class Q {
    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);
    }
}
class Producer implements Runnable {
    Q q;
    Producer(Q q) {
        this.q = q;
        new Thread(this, "Producer").start();
    }
    public void run() {
        int i = 0;
        while (true) { q.put(i++); }
    }
}
class Consumer implements Runnable {
    Q q;
    Consumer(Q q) {
        this.q = q;
        new Thread(this, "Consumer").start();
    }
    public void run() {
        while (true) { q.get(); }
    }
}
class PC {
    public static void main(String args[]) {
        Q q = new Q();
        new Producer(q);
        new Consumer(q);
        System.out.println("Press Control-C to stop.");
    }
}

可能出现:Put: 1 之后 Got: 1 连打五次,然后 Put: 2 到 7 消费者根本来不及拿。输出随 CPU 和负载变化。

二、正确写法:wait / notify 对向握手

用 valueSet 标记格子里有没有新值。get 发现没有就 wait,取走后 notify 生产者;put 发现还没被取走就 wait,放入后 notify 消费者。条件用 while 而不是 if,避免虚假唤醒后状态已变仍往下走。(原文正确示例括号错位,这里按语义修好。)

class Q {
    int n;
    boolean valueSet = false;
    synchronized int get() {
        while (!valueSet) {
            try { wait(); } catch (InterruptedException e) {
                System.out.println("InterruptedException caught");
            }
        }
        System.out.println("Got: " + n);
        valueSet = false;
        notify();
        return n;
    }
    synchronized void put(int n) {
        while (valueSet) {
            try { wait(); } catch (InterruptedException e) {
                System.out.println("InterruptedException caught");
            }
        }
        this.n = n;
        valueSet = true;
        System.out.println("Put: " + n);
        notify();
    }
}
class Producer implements Runnable {
    Q q;
    Producer(Q q) {
        this.q = q;
        new Thread(this, "Producer").start();
    }
    public void run() {
        int i = 0;
        while (true) { q.put(i++); }
    }
}
class Consumer implements Runnable {
    Q q;
    Consumer(Q q) {
        this.q = q;
        new Thread(this, "Consumer").start();
    }
    public void run() {
        while (true) { q.get(); }
    }
}
class PCFixed {
    public static void main(String args[]) {
        Q q = new Q();
        new Producer(q);
        new Consumer(q);
        System.out.println("Press Control-C to stop.");
    }
}

正确输出成对出现:Put: 1 / Got: 1 / Put: 2 / Got: 2 … 生产者不会超车,消费者也不会把同一个数拿两遍。

一句话总结:synchronized 只能互斥,不能表达“等到有数据”;生产者消费者要在同步方法里 wait / notify 对向握手,条件用 while 判断。

转载请注明来源:Java线程间通信
本文链接地址:https://ai.zhousir.top/?p=223

上一篇:

下一篇:

回复 取消