我很难理解将wait()
放在Object
课程中的概念。为了这个问题,请考虑wait()
和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上的锁定,然后执行等待,以满足我们需要在使用等待之前获取对象的锁定的等待条件,因此对于notifyAll我们已经在第31行获取了对象的对象的锁定
答案 0 :(得分:94)
我只是很难理解在对象类中放置wait()背后的概念为了这个问题,请考虑好像wait()和notifyAll()在线程类中
在Java语言中,您wait()
的特定实例上的Object
- 分配给该对象的监视器是精确的。如果要向正在该特定对象实例上等待的一个线程发送信号,则在该对象上调用notify()
。如果要向等待该对象实例的所有线程发送信号,请在该对象上使用notifyAll()
。
如果wait()
和notify()
位于Thread
上,那么每个线程都必须知道每个其他线程的状态。 thread1如何知道thread2正在等待访问特定资源?如果thread1需要调用thread2.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个线程在外部资源上阻塞太多,然后您可以增加数量。推动受攻击的服务器太难,然后减少运行线程的数量。锁定对象确保线程之间的互斥和通信,与运行的线程数无关。
答案 1 :(得分:37)
为了更好地理解为什么wait()和notify()方法属于Object类,我将给你一个真实的例子: 假设一个加油站有一个单独的厕所,其钥匙放在服务台。厕所是通过驾驶者的共享资源。为了使用该共享资源,预期用户必须获得上厕所锁的钥匙。用户前往服务台获取钥匙,打开门,从内部锁定并使用设施。
同时,如果第二个潜在用户到达加油站,他会发现厕所已锁定,因此无法使用。他去服务台,但钥匙不在那里,因为它掌握在当前用户手中。当前用户完成后,他解锁门并将钥匙返回服务台。他并不打算等待客户。服务台为等待的客户提供钥匙。如果在厕所被锁定时有多个预期用户出现,他们必须形成一个队列,等待锁的钥匙。每个帖子都不知道谁在厕所里。
显然,在将此类比应用于Java时,Java线程是用户,而厕所是线程希望执行的代码块。 Java提供了一种方法来锁定当前使用synchronized关键字执行它的线程的代码,并使其他希望使用它的线程等到第一个线程完成。这些其他线程处于等待状态。 Java并不像服务站那样公平,因为没有等待线程的队列。无论他们要求的顺序如何,任何一个等待线程都可以接下来监视器。唯一的保证是所有线程迟早都会使用受监控的代码。
最后你的问题的答案:锁可能是关键对象或服务台。这些都不是一个线程。
然而,这些是目前决定马桶是锁定还是打开的物体。这些对象可以通知浴室已打开(“通知”)或要求人们在等待锁定时等待。
答案 2 :(得分:5)
这个问题的其他答案都错过了关键点,在Java中,有一个与每个对象相关联的互斥锁。 (我假设您知道互斥锁或“锁定”是什么。)在大多数具有“锁定”概念的编程语言中,不的情况。例如,在Ruby中,您必须根据需要显式创建尽可能多的Mutex
个对象。
我想我知道为什么Java的创造者做出了这个选择(虽然,在我看来,这是一个错误)。原因与包含synchronized
关键字有关。我相信Java的创造者(天真)认为通过在语言中包含synchronized
方法,人们可以轻松编写正确的多线程代码 - 只需将所有共享状态封装在对象中,声明方法即可访问该状态为synchronized
,你就完成了!但它并没有那么成功......
无论如何,由于任何类都可以使用synchronized
方法,因此每个对象需要一个互斥锁,synchronized
方法可以锁定和解锁。
wait
和notify
都依赖于互斥锁。也许你已经明白为什么会出现这种情况......如果不是我可以添加更多解释,但是现在,让我们说两种方法都需要在互斥锁上工作。每个Java对象都有一个互斥锁,因此可以在任何Java对象上调用wait
和notify
。这意味着他们需要被声明为Object
的方法。
另一个选择是将静态方法放在Thread
或其他东西上,这会将任何Object
作为参数。对于新的Java程序员来说,这可能会少得多。但他们并没有这样做。改变任何这些决定都为时已晚;太糟糕了!
答案 3 :(得分:4)
回答你的第一个问题是因为java中的每个对象只有一个lock(monitor)
和wait(),notify(),notifyAll()
用于监视器共享,这就是为什么它们是Object
类的一部分而不是{{1 }}类。
答案 4 :(得分:1)
简单来说,原因如下。
Object
有监听。Object
。对于synchronized
方法/块,只有一个线程可以同时保存对象监视器。wait(), notify() and notifyAll()
类中的Object
方法允许在object
上创建的所有线程与其他synchronized or Lock
API)和通信(wait() and notify()
)是两个不同的概念。 如果Thread
类包含wait(), notify() and notifyAll()
方法,则会产生以下问题:
Thread
沟通问题Synchronization
是不可能的。如果每个线程都有监视器,我们将无法实现同步Inconsistency
处于对象状态有关详细信息,请参阅此article。
答案 5 :(得分:0)
这些方法适用于锁,而锁与Object相关联,而不是与Threads相关联。因此,它在Object类中。
方法wait(),notify()和notifyAll()不仅仅是方法,它们是同步实用程序,用于Java中线程之间的通信机制。
有关详细说明,请访问: http://parameshk.blogspot.in/2013/11/why-wait-notify-and-notifyall-methods.html
答案 6 :(得分:0)
这只是我在这个问题上的2美分......不确定这是否完整。
每个对象都有一个监视器和等待集 - &gt;一组线程(这可能更多在OS级别)。这意味着监视器和waitset可以被视为对象的私有成员。在Thread类中使用wait()和notify()方法意味着允许公共访问waitset或使用get-set方法来修改waitset。你不想这样做,因为那是糟糕的设计。
现在假定Object知道等待它的监视器的线程,它应该是Object的工作去唤醒那些等待它的线程,而不是一个线程类的对象进入并唤醒它们中的每一个(只有在线程类对象被赋予对waitset的访问权限时才有可能。但是,并不是特定线程的工作去唤醒每个等待线程。 (如果所有这些方法都在Thread类中,这正是会发生的情况)。它的工作就是释放锁定并继续完成自己的任务。线程独立工作,不需要知道其他线程正在等待对象监视器(这是线程类对象的不必要的细节)。如果它开始唤醒每个线程本身......它正在远离它的核心功能,即执行自己的任务。当您考虑可能存在1000个线程的场景时,您可以假设它可以创建多少性能影响。因此,假设Object Class知道谁正在等待它,它可以执行唤醒等待线程的工作,并且发送notify()的线程可以执行其进一步处理。
进行类比(也许不是正确的,但不能想到其他任何东西)。当我们停电时,我们会致电该公司的客户代表,因为她知道有合适的人员来联系以解决问题。作为消费者,您不能知道谁是其背后的工程师,即使您知道,也不可能打电话给他们每个人并告诉他们您的麻烦(这不是您的责任。您的职责是告知他们有关停电和CR的工作是去通知(唤醒)合适的工程师。)
让我知道这听起来是否正确......(我确实有时会混淆我的话语。)
答案 7 :(得分:0)
等待并通知操作隐式锁定,隐式锁定使线程间通信成为可能。并且所有对象都有自己的隐式对象副本。所以保持等待并通知隐含锁定生活的地方是一个好的决定。
另外,wait和notify也可以存在于Thread类中。我们可能不得不调用Thread.getCurrentThread()。wait(),而不是wait()。 对于等待和通知操作,有两个必需参数,一个是等待或通知其他的线程是对象的隐式锁定。两者都可以在Object和线程类中使用。 Thread类中的wait()方法与在Object类中执行的操作相同,将当前线程转换为等待状态等待它上次获取的锁。
所以是的,我认为等待和通知也可以在Thread类中出现,但它更像是一个将它保存在对象类中的设计决策。
答案 8 :(得分:0)
wait-wait方法告诉当前线程放弃监视器并进入休眠状态。
notify - 唤醒正在此对象监视器上等待的单个线程。
所以你看到wait()和notify()方法在监视器级别工作,当前持有监视器的线程被要求通过wait()方法和notify方法(或notifyAll)线程放弃该监视器。等待对象的监视器被通知线程可以唤醒。
此处需要注意的重点是,监视器被分配给不属于特定线程的对象。这就是为什么这些方法在Object类中的原因之一。 重申线程在Object的监视器上等待(锁定),并且还在一个对象上调用notify()来唤醒在Object的监视器上等待的线程。
答案 9 :(得分:-1)
wait()
方法将释放对指定对象的锁定,并在它可以检索锁定时等待。
notify()
,notifyAll()
将检查是否有线程等待获取对象的锁定,如果可能的话会将其提供给它们。
锁是对象的一部分的原因是资源(RAM)由Object
而不是Thread
定义。
理解这一点最容易的方法是Threads可以共享Objects(在这个例子中是所有线程共享的计算器),但是对象不能共享Attributes(就像基元一样,甚至引用自己也不会共享对象,他们只是指向同一个地方)。因此,为了确保只有一个线程可以修改对象,使用同步锁定系统
答案 10 :(得分:-1)
等待并通知方法总是在对象上调用,所以它是可能是Thread对象还是简单对象(不扩展Thread类) 给出的例子将清除你所有的疑虑。
我在类ObjB上调用了wait和notify,这是Thread类,所以我们可以说在任何对象上调用wait和notify。
public class ThreadA {
public static void main(String[] args){
ObjB b = new ObjB();
Threadc c = new Threadc(b);
ThreadD d = new ThreadD(b);
d.setPriority(5);
c.setPriority(1);
d.start();
c.start();
}
}
class ObjB {
int total;
int count(){
for(int i=0; i<100 ; i++){
total += i;
}
return total;
}}
class Threadc extends Thread{
ObjB b;
Threadc(ObjB objB){
b= objB;
}
int total;
@Override
public void run(){
System.out.print("Thread C run method");
synchronized(b){
total = b.count();
System.out.print("Thread C notified called ");
b.notify();
}
}
}
class ThreadD extends Thread{
ObjB b;
ThreadD(ObjB objB){
b= objB;
}
int total;
@Override
public void run(){
System.out.print("Thread D run method");
synchronized(b){
System.out.println("Waiting for b to complete...");
try {
b.wait();
System.out.print("Thread C B value is" + b.total);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}