如何在不是线程的对象上调用wait()
和方法?notify()
这真的没有意义,不是吗?
当然,这一定是有意义的,因为这两种方法可用于所有 Java 对象。有人可以提供解释吗?我无法理解如何使用wait()
和进行线程之间的通信notify()
。
如何在不是线程的对象上调用wait()
和方法?notify()
这真的没有意义,不是吗?
当然,这一定是有意义的,因为这两种方法可用于所有 Java 对象。有人可以提供解释吗?我无法理解如何使用wait()
和进行线程之间的通信notify()
。
锁定是关于保护共享数据。
锁在被保护的数据结构上。线程是访问数据结构的事物。锁在数据结构对象上,以防止线程以不安全的方式访问数据结构。
任何对象都可以用作内在锁(与 结合使用的意思synchronized
)。这样,您可以通过将同步修饰符添加到访问共享数据的方法来保护对任何对象的访问。
wait
和notify
方法在被用作锁的对象上调用。锁是一个共享的通信点:
当一个拥有锁的线程调用notifyAll
它时,等待同一个锁的其他线程会得到通知。当一个有锁的线程调用notify
它时,等待同一个锁的线程之一会得到通知。
当一个有锁的线程调用wait
它时,该线程释放锁并进入休眠状态,直到a)它收到通知,或者b)它只是任意唤醒(“虚假唤醒”);由于这两个原因之一,等待线程一直停留在调用中等待直到它唤醒,然后线程必须重新获取锁才能退出等待方法。
请参阅有关受保护块的 Oracle 教程, Drop 类是共享数据结构,使用 Producer 和 Consumer 可运行对象的线程正在访问它。锁定 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();
想想用一个现实生活中的例子,洗手间。当您想在办公室使用洗手间时,您有两种选择来确保在您使用洗手间后没有其他人会来。
你会选择哪个选项?
是的,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如果我理解错了你的问题,我很抱歉(英语不是我的母语)
当您将一些代码放在同步块中时:
sychronized(lock){...}
想要执行此块内的任何内容的线程首先获取对象上的锁,并且一次只有一个线程可以执行锁定在同一对象上的代码。任何对象都可以用作锁,但您应该小心选择与范围相关的对象。例如,当您有多个线程向帐户添加某些内容时,它们都有一些代码在块内负责该操作,例如:
sychronized(this){...}
然后不会发生同步,因为它们都锁定在不同的对象上。相反,您应该使用帐户对象作为锁。现在考虑这些线程也有从帐户中提取的方法。在这种情况下,可能会出现想取款的线程遇到空帐户的情况。它应该等到有钱后再将锁释放给其他线程以避免死锁。这就是等待和通知方法的用途。在此示例中,遇到空帐户的线程会释放锁定并等待来自进行存款的某个线程的信号:
while(balance < amountToWithdraw){
lock.wait();
}
当其他线程存入一些钱时,它会向其他线程发出信号,等待同一个锁。(当然,负责存款和取款的代码必须在同一个锁上同步才能正常工作并防止数据损坏)。
balance += amountToDeposit;
lock.signallAll;
如您所见,方法等待和通知仅在同步块或方法中才有意义。
在 Java 中,所有的 Object 都实现了这两种方法,显然如果没有监视器,这两种方法是无用的。
实际上,wait
成员notify
函数不应该属于线程,它应该属于命名为来自posix线程的条件变量的东西。你可以看看 cpp 是如何包装它的,它将它包装到一个专用的类std::condition_variable中。
Java没有做这种封装,而是以更高级的方式包装条件变量:monitor(直接将功能放入Object类)。
如果您不知道监视器或条件变量,那么,这确实会让人一开始就感到困惑。
类比: Java 线程是用户,厕所是线程希望执行的代码块。Java 提供了一种方法来锁定当前正在执行的线程的代码,该线程使用同步化的 keywokd,并让希望使用它的其他线程等到第一个线程完成。这些其他线程处于等待状态。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 美元的商品。这里无论哪个通道首先访问帐户执行事务,首先获取帐户上的锁,另一个通道将等待直到事务完成并释放帐户上的锁,因为无法知道哪个通道已经获得锁以及哪个通道正在等待获取锁。因此锁定总是应用于帐户本身而不是通道。在这里,我们可以将帐户视为对象,将通道视为线程。