繁体   English   中英

如何在不是线程的对象上调用 wait() 和 notify() 方法?

[英]How can the wait() and notify() methods be called on Objects that are not threads?

如何在不是线程的对象上调用wait()notify()方法? 这真的没有意义,不是吗?

当然,这一定是有意义的,因为这两种方法可用于所有 Java 对象。 有人可以提供解释吗? 我无法理解如何使用wait()notify()在线程之间进行通信。

锁定是关于保护共享数据。

锁在被保护的数据结构上。 线程是访问数据结构的东西。 锁位于数据结构对象上,以防止线程以不安全的方式访问数据结构。

任何对象都可以用作内在锁(意味着与synchronized结合使用)。 通过这种方式,您可以通过向访问共享数据的方法添加同步修饰符来保护对任何对象的访问。

对用作锁的对象调用waitnotify方法。 锁是一个共享的通信点:

  • 当一个拥有锁的线程调用notifyAll时,其他等待同一个锁的线程会得到通知。 当一个拥有锁的线程调用notify时,等待同一个锁的线程之一会得到通知。

  • 当有锁的线程调用wait时,该线程释放锁并进入休眠状态,直到 a) 它收到通知,或 b) 它只是任意唤醒(“虚假唤醒”); 由于这两个原因之一,等待线程一直停留在等待调用中,直到它被唤醒,然后线程必须重新获取锁才能退出等待方法。

请参阅有关受保护块Oracle 教程,Drop 类是共享数据结构,使用生产者和消费者可运行的线程正在访问它。 锁定 Drop 对象控制线程如何访问 Drop 对象的数据。

线程在 JVM 实现中被用作锁,建议应用程序开发人员避免将线程用作锁。 例如, Thread.join文档说:

此实现使用以 this.isAlive 为条件的 this.wait 调用循环。 当线程终止时,调用 this.notifyAll 方法。 建议应用程序不要在 Thread 实例上使用 wait、notify 或 notifyAll。

Java 5 引入了实现java.util.concurrent.locks.Lock显式锁。 这些比隐式锁更灵活; 有类似于等待和通知(等待和信号)的方法,但它们是在条件上,而不是在锁上。 具有多个条件可以仅针对等待特定类型通知的线程。

您可以使用wait()notify()来同步您的逻辑。 举个例子

synchronized (lock) {
    lock.wait(); // Will block until lock.notify() is called on another thread.
}

// Somewhere else...
...
synchronized (lock) {
    lock.notify(); // Will wake up lock.wait()
}

lock是类成员Object lock = new Object();

想想使用一个现实生活中的例子,一个洗手间 当您想在办公室使用洗手间时,您有两种选择,以确保您使用后没有其他人会来洗手间。

  1. 锁上洗手间的门,这样其他人在试图打开门时就会知道它被其他人使用了
  2. 去找办公室里的每个人,把他们锁在椅子(或桌子,或其他什么)上,去洗手间。

你会选择哪个?

是的,Javaland 也是一样!

所以在上面的故事中,

  • 洗手间 = 您要锁定的对象(只有您需要使用)
  • 您的员工同事 = 您想排除的其他线程

所以就像在现实生活中一样,当你有一些私人事务时,你锁定那个对象。 当您完成该对象时,您松开锁!

(是的,是的!,这是对发生的事情的非常简单的描述。当然真正的概念与此略有不同,但这是一个起点)

您可以根据需要使用静态Thread类方法sleep()停止线程一段时间。

public class Main {
    //some code here

    //Thre thread will sleep for 5sec.
    Thread.sleep(5000);   
}

如果你想停止一些对象,你需要在syncronized块中调用这个方法。

public class Main {

//some code

public void waitObject(Object object) throws InterruptedException {
    synchronized(object) {
        object.wait();
    }
}

public void notifyObject(Object object) throws InterruptedException {
    synchronized(object) {
        object.notify();
    }
}

}

PS如果我理解错了你的问题,我很抱歉(英语不是我的母语)

当你在 synchronized 块中放入一些代码时:

 sychronized(lock){...}

想要执行此块内任何内容的线程首先获取对象上的锁,并且一次只有一个线程可以执行锁定在同一对象上的代码。 任何对象都可以用作锁,但您应该小心选择与作用域相关的对象。 例如,当您有多个线程向帐户添加内容时,它们都有一些代码负责在一个块中执行此操作,例如:

sychronized(this){...}

然后没有同步发生,因为它们都锁定在不同的对象上。 相反,您应该使用帐户对象作为锁。 现在考虑这些线程也有从帐户中提取的方法。 在这种情况下,可能会出现想要提取某些东西的线程遇到空帐户的情况。 它应该等到有一些钱并释放锁给其他线程以避免死锁。 这就是等待和通知方法的用途。 在这个例子中,遇到空账户的线程释放锁并等待来自某个进行存款的线程的信号:

while(balance < amountToWithdraw){
    lock.wait();
}

当其他线程存入一些钱时,它会向等待同一锁的其他线程发出信号。 (当然,负责存款和取款的代码必须在同一个锁上同步才能工作并防止数据损坏)。

balance += amountToDeposit;
lock.signallAll;

如您所见,方法 wait 和 notify 仅在同步块或方法中有意义。

在 Java 中所有 Object 都实现了这两个方法,显然如果没有监视器这两个方法是没有用的。

实际上, waitnotify成员函数不应该属于线程,它应该属于的东西命名为来自posix thread 的条件变量 你可以看看 cpp 如何包装它,它将它包装成一个专用的类std::condition_variable

Java 没有做这种封装,而是用更高级的方式包装条件变量: monitor (直接把功能放到Object类中)。

如果您不知道监视器或条件变量,那么一开始确实会让人们感到困惑。

  1. Wait 和 notify 不仅仅是普通的方法或同步实用程序,更重要的是它们是 Java 中两个线程之间的通信机制。 如果此机制无法通过任何 java 关键字(如同步)使用,则对象类是使它们可用于每个对象的正确位置。 请记住同步和等待通知是两个不同的领域,不要混淆它们是相同或相关的。 同步是提供互斥和保证Java 类的线程安全,如竞争条件,而wait 和notify 是两个线程之间的通信机制。
  2. 锁是基于每个对象提供的,这是在 Object 类而不是 Thread 类中声明 wait 和 notify 的另一个原因。
  3. 在 Java 中,为了进入代码的临界区,线程需要锁并等待锁,它们不知道哪个线程持有锁,而只知道锁被某个线程持有,它们应该等待锁而不是知道哪个线程持有锁线程在同步块内并要求他们释放锁。 这个类比适合等待和通知在对象类上而不是 Java 中的线程。

类比: Java 线程是用户,厕所是线程希望执行的代码块。 Java 提供了一种使用同步密钥锁定当前正在执行它的线程的代码的方法,并使希望使用它的其他线程等待直到第一个线程完成。 这些其他线程处于等待状态。 Java 不像服务站那样公平,因为没有等待线程的队列。 任何一个等待线程都可以获取下一个监视器,而不管它们要求的顺序如何。 唯一的保证是所有线程迟早都会使用受监控的代码。

来源

如果您查看以下生产者和消费者代码:
sharedQueue对象充当producer and consumer线程之间的线程间通信。

import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;

public class ProducerConsumerSolution {

    public static void main(String args[]) {
        Vector<Integer> sharedQueue = new Vector<Integer>();
        int size = 4;
        Thread prodThread = new Thread(new Producer(sharedQueue, size), "Producer");
        Thread consThread = new Thread(new Consumer(sharedQueue, size), "Consumer");
        prodThread.start();
        consThread.start();
    }
}

class Producer implements Runnable {

    private final Vector<Integer> sharedQueue;
    private final int SIZE;

    public Producer(Vector<Integer> sharedQueue, int size) {
        this.sharedQueue = sharedQueue;
        this.SIZE = size;
    }

    @Override
    public void run() {
        for (int i = 0; i < 7; i++) {
            System.out.println("Produced: " + i);
            try {
                produce(i);
            } catch (InterruptedException ex) {
                Logger.getLogger(Producer.class.getName()).log(Level.SEVERE, null, ex);
            }

        }
    }

    private void produce(int i) throws InterruptedException {

        // wait if queue is full
        while (sharedQueue.size() == SIZE) {
            synchronized (sharedQueue) {
                System.out.println("Queue is full " + Thread.currentThread().getName() + " is waiting , size: "
                        + sharedQueue.size());

                sharedQueue.wait();
            }
        }

        // producing element and notify consumers
        synchronized (sharedQueue) {
            sharedQueue.add(i);
            sharedQueue.notifyAll();
        }
    }
}

class Consumer implements Runnable {

    private final Vector<Integer> sharedQueue;
    private final int SIZE;

    public Consumer(Vector<Integer> sharedQueue, int size) {
        this.sharedQueue = sharedQueue;
        this.SIZE = size;
    }

    @Override
    public void run() {
        while (true) {
            try {
                System.out.println("Consumed: " + consume());
                Thread.sleep(50);
            } catch (InterruptedException ex) {
                Logger.getLogger(Consumer.class.getName()).log(Level.SEVERE, null, ex);
            }

        }
    }

    private int consume() throws InterruptedException {
        //wait if queue is empty
        while (sharedQueue.isEmpty()) {
            synchronized (sharedQueue) {
                System.out.println("Queue is empty " + Thread.currentThread().getName()
                                    + " is waiting , size: " + sharedQueue.size());

                sharedQueue.wait();
            }
        }

        //Otherwise consume element and notify waiting producer
        synchronized (sharedQueue) {
            sharedQueue.notifyAll();
            return (Integer) sharedQueue.remove(0);
        }
    }
}

来源

“此方法只能由作为此对象监视器所有者的线程调用。” 所以我认为你必须确保有一个线程是对象上的监视器。

对象类是为每个对象提供锁的正确位置。 假设有一个联名银行账户,因此多个用户可以使用同一个账户通过多个渠道进行交易。 目前,该账户的余额为 1500/-,账户中保留的最低余额为 1000/-。 现在,第一个用户试图通过 ATM 提取 500/- 的金额,另一个用户试图通过刷卡机购买价值 500/- 的任何商品。 这里首先访问账户进行交易的通道首先获取账户的锁,另一个通道将等待交易完成并释放账户的锁,因为无法知道哪个通道已经获得了锁以及哪个通道正在等待获取锁。 因此,锁定始终应用于帐户本身而不是频道。 在这里,我们可以将帐户视为对象,将通道视为线程。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM