将 wait()、notify() 方法放入 Object 类背后的概念

2022-08-31 16:46:21

我只是很难理解上课背后的概念。为了这个问题,考虑是否和在课堂上。wait()Objectwait()notifyAll()Thread

class Reader extends Thread {
    Calculator c;
    public Reader(Calculator calc) {
        c = calc;
    }

    public void run() {
        synchronized(c) {                              //line 9
        try {
            System.out.println("Waiting for calculation...");
            c.wait();
        } catch (InterruptedException e) {}
            System.out.println("Total is: " + c.total);
        }
    }

    public static void main(String [] args) {
        Calculator calculator = new Calculator();
        new Reader(calculator).start();
        new Reader(calculator).start();
        new Reader(calculator).start();
        calculator.start();
    }
}

class Calculator extends Thread {
    int total;
    public void run() {
        synchronized(this) {                     //Line 31
            for(int i=0;i<100;i++) {
                total += i;
            }
             notifyAll();
        }
    } 
}

我的问题是,它本可以产生什么不同?在第 9 行中,我们在对象 c 上获取锁,然后执行等待,这满足等待条件,即在使用 wait 之前,我们需要在对象上获取锁,因此对于 notify 也是如此,我们在第 31 行的计算器对象上获取了锁。


答案 1

我只是很难理解将wait()放在对象类中背后的概念 对于这个问题,请考虑好像wait()和通知All()都在线程类中

在Java语言中,您在一个特定的实例上 - 一个精确分配给该对象的监视器。如果要向正在等待该特定对象实例的线程发送信号,则调用该对象。如果要向正在等待该对象实例的所有线程发送信号,请在该对象上使用。wait()Objectnotify()notifyAll()

如果 和 位于 上,则每个线程都必须知道其他每个线程的状态。线程 1 如何知道线程 2 正在等待对特定资源的访问?如果 thread1 需要调用,它必须以某种方式找出正在等待的内容。线程需要有某种机制来注册它们所需的资源或操作,以便其他人可以在内容准备就绪或可用时向他们发出信号。wait()notify()Threadthread2.notify()thread2

在Java中,对象本身是在线程之间共享的实体,它允许它们相互通信。这些线程彼此之间没有特定的知识,它们可以异步运行。它们运行,并锁定、等待并通知要访问的对象。他们不了解其他线程,也不需要知道其状态。他们不需要知道是 thread2 在等待资源 - 他们只需通知资源,无论谁在等待(如果有的话)都会收到通知。

在 Java 中,我们使用对象作为线程之间的同步、互斥和通信点。我们在对象上进行同步,以获得对重要代码块的互斥访问并同步内存。如果我们在等待某些条件发生变化,我们会等待一个对象 - 一些资源变得可用。如果我们想唤醒休眠线程,我们会在对象上发出通知。

// locks should be final objects so the object instance we are synchronizing on,
// never changes
private final Object lock = new Object();
...
// ensure that the thread has a mutex lock on some key code
synchronized (lock) {
    ...
    // i need to wait for other threads to finish with some resource
    // this releases the lock and waits on the associated monitor
    lock.wait();
    ...
    // i need to signal another thread that some state has changed and they can
    // awake and continue to run
    lock.notify();
}

程序中可以有任意数量的锁定对象 - 每个对象锁定一个特定的资源或代码段。您可能有 100 个锁定对象,只有 4 个线程。当线程运行程序的各个部分时,它们将获得对其中一个锁定对象的独占访问权限。同样,他们不必知道其他线程的运行状态。

这允许您根据需要增加或减少软件中运行的线程数。您发现 4 个线程在外部资源上阻塞了太多,然后您可以增加该数字。将受破坏的服务器推得太紧,然后减少正在运行的线程数。锁定对象确保互斥体和线程之间的通信,而与正在运行的线程数无关。


答案 2

为了更好地理解为什么 wait() 和 notify() 方法属于 Object 类,我将给你一个现实生活中的例子:假设一个加油站有一个厕所,其密钥保存在服务台。厕所是路过的驾驶者的共享资源。要使用此共享资源,潜在用户必须获取马桶锁的钥匙。用户前往服务台并获取钥匙,打开门,从内部锁定并使用设施。

与此同时,如果第二个潜在用户到达加油站,他会发现厕所被锁上了,因此他无法使用。他去了服务台,但钥匙不在那里,因为它在当前用户的手中。当前用户完成操作后,他会打开门并将钥匙归还给服务台。他懒得等顾客。服务台将钥匙交给等待的顾客。如果在厕所被锁定时有多个潜在用户出现,他们必须形成一个队列,等待锁的钥匙。每个线程都不知道谁在厕所里。

显然,在将此类比应用于Java时,Java线程是用户,而马桶是线程希望执行的代码块。Java 提供了一种方法来锁定当前正在使用 sync 关键字执行它的线程的代码,并使其他希望使用它的线程等待第一个线程完成。这些其他线程处于等待状态。Java不像服务站那样公平,因为没有等待线程的队列。任何一个等待线程都可能获得下一个监视器,无论他们要求的顺序如何。唯一的保证是所有线程迟早都会使用受监视的代码。

最后,您的问题的答案是:锁可以是钥匙对象或服务台。它们都不是线程。

但是,这些是当前决定厕所是锁定还是打开的对象。这些是能够通知浴室已打开(“通知”)或要求人们在锁定时等待的物体。