You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

生产者消费者问题中消费者获旧值原因及线程对象疑问

问题描述

代码实现

class Q2 {
    int n;
    boolean valueSet = false;

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

    synchronized void put(int n) {
        while(valueSet)
            try {
                System.out.println("Producer waiting ...");
                wait();
            }
            catch(InterruptedException e) {
                System.err.println("InterruptedException caught");
            }
        System.out.println("Producer awakened");
        System.out.println("Before put n is: " + this.n);
        this.n = n;
        valueSet = true;
        System.out.println("Put: " + this.n);
        notify();
        System.out.println("Producer called notify()");
    }
}

class Producer2 implements Runnable {
    Q2 q;
    int noOfTimes;

    Producer2(Q2 q) {
        this.q = q;
        new Thread(this, "Producer").start();
    }

    public void run() {
        int i=0;
        noOfTimes=0;
        while(q.n < 2) {
            q.put(i++);
            noOfTimes++;
        }
        System.out.println("Producer ran: " + noOfTimes + " times.");
    }
}

class Consumer2 implements Runnable {
    Q2 q;
    int noOfTimes;

    Consumer2(Q2 q) {
        this.q = q;
        new Thread(this, "Consumer").start();
    }

    public void run() {
        int i=0;
        noOfTimes=0;
        while(q.n < 2) {
            System.out.println("Iteration " + (noOfTimes+1) + "; Before get() n is: " + q.n);
            int val = q.get();
            System.out.println("After get() n is: " + q.n);
            noOfTimes++;
        }
        System.out.println("Consumer ran: " + noOfTimes + " times.");
        System.out.println("n: " + q.n);
    }
}

public class PCFixed {

    public static void main(String[] args) {
        Q2 q = new Q2();
        new Producer2(q);
        new Consumer2(q); 
    }
}

某次运行输出

Producer awakened
Before put n is: 0 // because n is int and default value of n is 0
Put: 0 // instance variable n associated with object of Q2 is set to 0
Producer called notify() // no effect
Producer waiting ... // thread spawned from Producer waits
Iteration 1; Before get() n is: 0 // context switching to thread spawned from Consumer
Consumer awakened
Got: 0
Consumer called notify()
After get() n is: 0
Iteration 2; Before get() n is: 0
Producer awakened // context switching to Producer's thread
Before put n is: 0
Put: 1
Producer called notify()
Producer waiting ... // Producer's thread waits/sleeps
Consumer awakened // context switching to Consumer's thread
Got: 1
Consumer called notify()
After get() n is: 1
Producer awakened // context switching to Producer's thread
Before put n is: 1
Put: 2 // #### line 1
Producer called notify()
Iteration 3; Before get() n is: 1 // #### line 2: context switching to Consumer's thread
Producer ran: 3 times. // context switching to Producer's thread
Consumer awakened // context switching to Consumer's thread
Got: 2
Consumer called notify()
After get() n is: 2
Consumer ran: 3 times.
n: 2 // Consumer's thread ends here

疑问

在上述输出中,生产者已在line1处将共享Q2实例的n值设为2,但消费者在line2处获取到的n值却为1。由于生产者与消费者线程共享同一个Q2实例,为何修改后的n值未在line2处及时体现?同时请解释threaded object的含义。若首次运行未出现该现象,多次运行即可复现该问题。


解答

1. 为什么n值未及时体现?

核心原因是线程可见性缺失和非同步的变量访问:

  • Q2类的n变量既没有用volatile修饰,消费者线程在run()方法的while(q.n < 2)循环、以及打印语句中访问n时,也没有进入同步块/方法。
  • Java内存模型中,每个线程会缓存共享变量的副本到自己的工作内存。如果没有同步机制(synchronized或volatile),生产者线程修改主内存中n的值后,不会立即同步到消费者线程的工作内存,导致消费者读取到旧的缓存值(1)。
  • 另外,消费者的while(q.n < 2)循环条件没有同步保护,即使生产者已经把n改成2,消费者线程可能还在使用缓存的旧值,继续进入循环执行打印操作,从而输出1。

简单来说:你在非同步场景下直接读取共享变量q.n,无法保证能看到其他线程对该变量的最新修改,因此出现了旧值。

2. Threaded Object的含义

Threaded Object指的是被多个线程共享、用于线程间交互的对象,是线程间传递数据或完成同步的核心载体。在你的代码中,Q2的实例q就是典型的threaded object:

  • 它被生产者和消费者线程共同持有,两个线程通过调用它的synchronized方法(put、get)完成数据传递和等待唤醒的同步逻辑。
  • 这类对象通常包含共享状态(比如代码中的n和valueSet),并依赖同步机制保证多线程访问时的安全性和状态一致性。

内容的提问来源于stack exchange,提问作者Shri

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 00:20:56