Java 为什么我的消费者不使用整数值?(生产者消费者等待并通知)

Java 为什么我的消费者不使用整数值?(生产者消费者等待并通知),java,multithreading,Java,Multithreading,我有两条线。一个是生产者级产品,第二个是消费者级产品。我想模拟门生产者。所以生产商提供木材,消费者可以生产门。但我不知道如何在这两个线程之间进行通信。现在,当我运行我的程序时,只有木材被交付,但门没有生产。我不明白为什么 public class Deliver implements Runnable { private static int MAX_STOCKPILE = 15; private Integer wood; public Deliver(Integ

我有两条线。一个是生产者级产品,第二个是消费者级产品。我想模拟门生产者。所以生产商提供木材,消费者可以生产门。但我不知道如何在这两个线程之间进行通信。现在,当我运行我的程序时,只有木材被交付,但门没有生产。我不明白为什么

public class Deliver implements Runnable {

    private static int MAX_STOCKPILE = 15;

    private Integer wood;

    public Deliver(Integer wood) {
        this.wood = wood;
        new Thread(this, "Deliver").start();
    }

    public synchronized void deliver() throws InterruptedException{

        Thread.sleep(500);

        if (wood < MAX_STOCKPILE) {
            wood++;
            System.out.println("Wood delivered" + " | Wood stockpile: " + wood);
            notify();
        }
        else {
            wait();
        }
    }

    @Override
    public void run() {
        while (true) {
            try {
                deliver();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class Produce implements Runnable{

    private Integer wood;

    public Produce(Integer wood) {
        this.wood = wood;
        new Thread(this, "Produce").start();
    }

    public synchronized void produce() throws InterruptedException{
        Thread.sleep(1000);
        if (wood == 10) {
            wood -= 10; //produce
            System.out.println("Doors produced");
            notify();
        }
        else {
            wait();
        }
    }

    @Override
    public void run() {
        while (true) {
            try {
                produce();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class Main {

    public static void main(String[] args) {
        Integer wood = 0;
        new Deliver(wood);
        new Produce(wood);
    }
}
改变

if (wood == 10) { 


如果线程在=10时没有捕捉到它,需要注意的是整数是不可变的

更改对整数的引用时,将创建一个与前一个对象没有关系的新对象

您想要的是一个在两个线程之间共享的对象,因此当您更改值而不是引用时,它们会查看相同的值

e、 g

wood = Integer.valueOf(wood.intValue() - 10);
我建议使用AtomicInteger并将对它的引用设为最终引用,以确保您不会意外尝试更改引用


正如安德鲁·詹金斯所说;如果锁定、通知/等待不相关的对象,则没有任何线程安全性。一旦有了共享对象,就必须锁定、通知/等待该共享对象。

我将把我的解决方案加入到混合中,同时考虑到的是关于使用AtomicInteger的建议

import java.util.concurrent.atomic.AtomicInteger;

public class Main {

    public static void main(String[] args) {
        AtomicInteger wood = new AtomicInteger(0);
        new Deliver(wood);
        new Produce(wood);
    }
}

public class Deliver implements Runnable {

    private static int MAX_STOCKPILE = 15;

    private final AtomicInteger wood;

    public Deliver(AtomicInteger wood) {
        this.wood = wood;
        new Thread(this, "Deliver").start();
    }

    public void deliver() throws InterruptedException{

        Thread.sleep(500);
        synchronized(wood) {
            if (wood.intValue() < MAX_STOCKPILE) {
                wood.addAndGet(1);
                System.out.println("Wood delivered" + " | Wood stockpile: " + wood);
                wood.notify();
            } else {
                wood.wait();
            }
        }
    }

    @Override
    public void run() {
        while (true) {
            try {
                deliver();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class Produce implements Runnable{

    private final AtomicInteger wood;

    public Produce(AtomicInteger wood) {
        this.wood = wood;
        new Thread(this, "Produce").start();
    }

    public void produce() throws InterruptedException{
        synchronized(wood) {
            if (wood.intValue() >= 10) {
                wood.addAndGet(-10); //produce
                System.out.println("Doors produced");
                wood.notify();
            }
            else {
                wood.wait();
            }
        }
    }

    @Override
    public void run() {
        while (true) {
            try {
                produce();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
主要变化:

我们使用可变对象在线程之间进行通信。 我们在可变对象上同步,而不是在正在运行的线程上同步。 现在,当我运行我的程序时,只有木材被交付,但门没有生产。我不明白为什么

您的代码存在多个问题:

当您将一个实例方法标记为synchronized时,任何进入该方法的线程都将获得该方法的锁,即调用该方法的实例。因为这个in-Deliver引用一个Deliver实例,而这个in-product引用一个product实例,所以在这种情况下wait和notify调用实际上是无用的,因为它们对相同的对象不感兴趣。 Java中要记住的黄金法则是,它使用传递值语义。因此,原语和引用总是按值传递。虽然您可能假设Deliver和product都将修改从main传递给它们的同一个整数,但事实并非如此。
< P>以上所说的,我强烈建议您考虑使用类似ARARYBACKIGHANCE队列来解决这个问题,而不是用等待和通知来重新发明车轮。

您是否尝试过通过调试它来调试它?为什么您的消费者称为“产品”?我将使用一个第三类,称之为CircularBuffer,并将其置于其中。使用缓冲区而不是整数。现在看起来好像计数器可以工作,但如果制作人制作的每件商品都不一样呢?例如,流中形成句子的字符。单独存储每一个,以便每个都有不同的值。使用现成的类,一些实现BlockingQueue。让生产者调用put方法,消费者调用take方法。如果你想实现类似的类,首先学习java.util并发包中各种类的源代码,我很快就完成了,没有注意到我换错了类。因为我已经创建了一个新类,它既有方法生成,也有方法交付,而且你的答案很好,但是当方法在单独的类中时,就像在第一个post类中一样,它仍然不起作用。我很困惑作者的代码是如何工作的。除了引用问题之外,除非显式引用,否则notify和wait不能跨对象工作。我在下面添加了注释,说明我很忙,代码仍然不能工作。但我仍然不明白为什么我的对象不被共享?@PeterLawrey因为Java总是按值传递,如果OP使用int而不是整数,那么代码的行为不会有任何不同。我相信不变性是一个副作用,但根本原因是传递值语义。将wait和notify参数放在一边,并假设它已正确实现。此外,Integer类缓存值,因此不变性在这种情况下会产生影响吗?@CKing由于传递值,ypu必须传递一个对可以变异的值的引用。在这种情况下,缓存不会产生任何影响,但您是对的,您可能会遇到这个问题。@PeterLawrey假设整数是可变的。在这种特殊情况下会有什么不同吗?由于传递值语义,wood=wood+1不会更改传递给Deliverand的原始整数的值,并且此更改不会反映在生成器wood中。这里问题的根本原因是传递值IMO。此外,缓存是一个非问题吗?我认为一旦木材超出一定范围,就会引起问题。由于存在虚假的唤醒问题,等待调用必须在检查木材生产/消耗条件的循环中进行。您能详细说明吗?run正在创建一个循环。据我所知,它按预期工作。
wood = Integer.valueOf(wood.intValue() - 10);
import java.util.concurrent.atomic.AtomicInteger;

public class Main {

    public static void main(String[] args) {
        AtomicInteger wood = new AtomicInteger(0);
        new Deliver(wood);
        new Produce(wood);
    }
}

public class Deliver implements Runnable {

    private static int MAX_STOCKPILE = 15;

    private final AtomicInteger wood;

    public Deliver(AtomicInteger wood) {
        this.wood = wood;
        new Thread(this, "Deliver").start();
    }

    public void deliver() throws InterruptedException{

        Thread.sleep(500);
        synchronized(wood) {
            if (wood.intValue() < MAX_STOCKPILE) {
                wood.addAndGet(1);
                System.out.println("Wood delivered" + " | Wood stockpile: " + wood);
                wood.notify();
            } else {
                wood.wait();
            }
        }
    }

    @Override
    public void run() {
        while (true) {
            try {
                deliver();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class Produce implements Runnable{

    private final AtomicInteger wood;

    public Produce(AtomicInteger wood) {
        this.wood = wood;
        new Thread(this, "Produce").start();
    }

    public void produce() throws InterruptedException{
        synchronized(wood) {
            if (wood.intValue() >= 10) {
                wood.addAndGet(-10); //produce
                System.out.println("Doors produced");
                wood.notify();
            }
            else {
                wood.wait();
            }
        }
    }

    @Override
    public void run() {
        while (true) {
            try {
                produce();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}