get 和 put 方法是否只同步,以便我们可以在下面的代码中调用 notifyAll()

Are the methods get and put only synchronized so that we can call notifyAll() in the following code

我在尝试阅读生产者消费者解决方案时遇到了这段代码:

package SampleProjects;

public class ProducerConsumerTest {

    public static void main(String[] args) {
        CubbyHole c = new CubbyHole();
        Producer p1 = new Producer(c, 1);
        Consumer c1 = new Consumer(c, 1);
        p1.start();
        c1.start();
    }
}

class CubbyHole {

    private int contents;
    private boolean available = false;

    public synchronized int get() {
        while (available == false) {
            try {
                wait();
            } catch (InterruptedException e) {
            }
        }
        available = false;
        notifyAll();
        return contents;
    }

    public synchronized void put(int value) {
        while (available == true) {
            try {
                wait();
            } catch (InterruptedException e) {
            }
        }
        contents = value;
        available = true;
        notifyAll();
    }
}

class Consumer extends Thread {

    private final CubbyHole cubbyhole;
    private final int number;

    public Consumer(CubbyHole c, int number) {
        cubbyhole = c;
        this.number = number;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            int value = cubbyhole.get();
            System.out.println("Consumer #" + this.number + " got: " + value);
        }
    }
}

class Producer extends Thread {

    private final CubbyHole cubbyhole;
    private final int number;

    public Producer(CubbyHole c, int number) {
        cubbyhole = c;
        this.number = number;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            cubbyhole.put(i);
            System.out.println("Producer #" + this.number + " put: " + i);
            try {
                sleep((int) (Math.random() * 100));
            } catch (InterruptedException e) {
            }
        }
    }
}

在这段代码中,两个线程调用不同的方法,即生产者线程只关心 put 方法,消费者线程只关心 get 方法,所以我想知道为什么我们内部有循环时需要同步它们检查内容 "while(available)"

然后我删除了 synchronized 关键字,它抛出了一个 IllegalStateMonitor 异常

所以我的问题是我们是否只需要同步关键字以便我们可以调用 notifyAll()

因为两者 notifyAll and wait 都要求调用线程拥有调用该方法的对象上的监视器。

Throws:

IllegalMonitorStateException - if the current thread is not the owner of this object's monitor.

由于您在 this 上调用 notifyAllwait,您将需要 this 对象的监视器。 synchronized 实例方法给了你。

对方法进行同步有两个效果:

  • 它允许 wait 和 notifyAll 通过让 2 个线程共享控制通知的监视器来工作。在 CubbyHole 实例上调用 notifyAll 只会通知正在等待同一 CubbyHole 实例的线程。

  • 它为 CubbyHole 对象的实例成员建立了内存可见性,因此一旦线程拥有监视器,它就可以保证看到这些变量的当前值。 JVM 可以积极地缓存值或重新排序指令,它依靠像 synchronized 关键字这样的标记来知道它应该对这些优化施加什么限制。

将synchronized放在方法上意味着线程需要在开始执行该方法之前获取调用该方法的实例的监视器。所以 cubbyhole 对象上的 put 和 get 方法使用同一个监视器,如果一个线程正在执行其中一个方法,另一个线程将被阻止进入另一个。请注意,等待会释放线程对锁的持有(允许其他线程工作),等待线程必须重新获取监视器才能离开等待。

使用了synchronized关键字,这样就没有两个线程可以同时执行这些方法。只应允许一个线程访问 put 和 get 方法。持有 CubbyHole 锁的线程将能够执行 get 和 put 方法。

不同步get和put会出现错误的情况。两种场景举例-

  1. 如果可用 == false 并且两个生产者线程同时访问 put 方法。假设线程 1 将 10 分配给内容,线程 2 分配 20 并且 available == true。那么什么值消费者线程将获得 10 或 20?

  2. 如果可用 == true 并且两个消费者线程正在同时访问 get 方法。两者都将继续执行,因为 get 未同步,并且两者将获得相同的错误值。

同步put方法的目的是只有一个生产者线程可以生产,而其他生产者线程必须等待直到这个生产值被一个消费者线程消费。

同步get方法的目的是只有一个消费者线程可以读取生产值,而其他消费者线程必须等待,然后通过将可用设置为false来通知生产者生产。