简体   繁体   English

Java:wait()和notify()混淆

[英]Java: wait() and notify() confusion

I'm trying to implement a condition that only one thread to access: Let's say it's a bottle of water - I want only 1 person (thread) to be able to have it a time. 我正在尝试实现一个只能访问一个线程的条件:假设它是一瓶水-我只希望一个人(线程)能够有一个时间。 Everything seems to work smoothly but I can't get the printing to be shown - the one before the call to wait() ; 一切似乎都工作顺利,但我无法显示打印内容–调用wait()之前的内容; .

public synchronized void getBotttle  {
    while(myCondition) {
      try {
        System.out.println("Printing that is never done?!");
        wait();
      }
      catch (InterruptedException e) {}
    }

    System.out.println("Printing that works");
    myCondition = true;
    notifyAll(); //or notify(), tried both

    try {
      Thread.sleep(time); // 
    }
    catch (InterruptedException e) {}
    System.out.println("Printing again");
    methodToMakeConditionFalse();
   // notifyAll(); even if I put it here its still the same
}

This method is called by the threads and it works as intended - only 1 thread has the "bottle" but the printing isn't there. 该方法由线程调用,并且按预期方式工作-只有1个线程带有“瓶”,但没有打印。 Any ideas? 有任何想法吗?

Effectively answer is very simple, signature of your getBotttle() method has keyword synchronized which means never and ever two different threads will access this code simultaneously. 有效的答案很简单,你的签名getBotttle()方法的关键字synchronized ,这意味着永远永远远两个不同的线程会同时访问此代码。 So, whole block with while(myCondition) { ... } is fruitless. 因此,使用while(myCondition) { ... }整个块是徒劳的。

Secondly, I recommend you to look into java.util.concurrent.* package. 其次,建议您研究java.util.concurrent.*包。

UPD. UPD。 Seems worth to clarify what's usual usecase for wait/notifyAll is: 似乎有必要澄清wait / notifyAll的通常用例是:

public class WaitNotify {

    public static void main(String[] args) throws InterruptedException {
        new WaitNotify().go();
    }

    private void go() throws InterruptedException {
        ResourceProvider provider = new ResourceProvider();
        Consumer c1 = new Consumer("consumer1", provider);
        Consumer c2 = new Consumer("consumer2", provider);
        Consumer c3 = new Consumer("consumer3", provider);
        Consumer[] consumers = new Consumer[] { c1, c2, c3 };

        for (int i = 0; i < consumers.length; i++) {
            provider.grant(consumers[i]);
        }
    }

    public static class ResourceProvider {
        private Resource resource = new Resource();

        public synchronized void grant(Consumer consumer) throws InterruptedException {
            while (resource == null) {
                wait();
            }
            consumer.useResource(resource);
            resource = null;
        }

        public synchronized void putBack(Resource resource) {
            this.resource = resource;
            notifyAll();
        }
    }

    public static class Resource {
        public void doSomething(String consumer) {
            System.out.println("I'm working! " + consumer);
            try {
                Thread.sleep(3L * 1000L);
            } catch (InterruptedException e) { }
        }
    }

    public static class Consumer implements Runnable {
        private String consumer;
        private Resource resource;
        private ResourceProvider provider;

        public Consumer(String consumer, ResourceProvider provider) {
            this.consumer = consumer;
            this.provider = provider;
        }

        public void useResource(Resource r) {
            this.resource = r;
            new Thread(this).start();
        }

        @Override
        public void run() {
            resource.doSomething(consumer);
            provider.putBack(resource);
        }
    }
}

You don't have a complete example, which makes it hard to tell what you're doing wrong; 您没有完整的示例,这很难说出您在做什么。 my guess is that your condition flag isn't set appropriately. 我的猜测是您的条件标记设置不正确。 Here is a complete example which works, it makes sure that only one thread has access to a resource at a time. 这是一个有效的完整示例,它确保一次只有一个线程可以访问资源。

public class StuffExample {

    public static void main(String[] args) throws Exception {

        Worker worker = new Worker(new StuffHolder());
        Thread t1 = new Thread(worker); 
        Thread t2 = new Thread(worker); 

        t1.start();
        t2.start();

        Thread.sleep(10000L);
        t1.interrupt();
        t2.interrupt();
    }
}

class Worker implements Runnable {
    private StuffHolder holder;

    public Worker(StuffHolder holder) {
        this.holder = holder;
    }

    public void run() {
        try {
            while (!Thread.currentThread().isInterrupted()) {
                holder.useStuff();
                Thread.sleep(1000L);
            }
        }
        catch (InterruptedException e) {
        }
    }
}

class StuffHolder {

    private boolean inUse = false;
    private int count = 0;
    public synchronized void useStuff() throws InterruptedException {
        while (inUse) {
            wait();
        }
        inUse = true;
        System.out.println("doing whatever with stuff now, count=" 
            + count + ", thread=" + Thread.currentThread().getName());
        count += 1;
        inUse = false;
        notifyAll();
    }   
}

Output is: 输出是:

doing whatever with stuff now, count=0, threadid=Thread-0
doing whatever with stuff now, count=1, threadid=Thread-1
doing whatever with stuff now, count=2, threadid=Thread-0
doing whatever with stuff now, count=3, threadid=Thread-1
doing whatever with stuff now, count=4, threadid=Thread-0
doing whatever with stuff now, count=5, threadid=Thread-1
doing whatever with stuff now, count=6, threadid=Thread-0
doing whatever with stuff now, count=7, threadid=Thread-1
doing whatever with stuff now, count=8, threadid=Thread-0
doing whatever with stuff now, count=9, threadid=Thread-1
doing whatever with stuff now, count=10, threadid=Thread-0
doing whatever with stuff now, count=11, threadid=Thread-1
doing whatever with stuff now, count=12, threadid=Thread-0
doing whatever with stuff now, count=13, threadid=Thread-1
doing whatever with stuff now, count=14, threadid=Thread-0
doing whatever with stuff now, count=15, threadid=Thread-1
doing whatever with stuff now, count=16, threadid=Thread-1
doing whatever with stuff now, count=17, threadid=Thread-0
doing whatever with stuff now, count=18, threadid=Thread-1
doing whatever with stuff now, count=19, threadid=Thread-0

See Oracle's tutorial on guarded blocks . 请参阅有关受保护块的Oracle教程

A big thanks to both of you. 非常感谢你们俩。 I will try write everything clear so other people stuck at something similar can work it out. 我会尝试将所有内容写清楚,以使其他陷入类似困境的人可以解决。

What I had is 2 Threads (lets say 2 people). 我只有2个线程(可以说2个人)。 They both have to drink water from 1 bottle, so when the bottle is in use the 2nd person has to wait. 他们俩都必须从1瓶中喝水,因此在使用该瓶时,第二个人必须等待。 My code looked roughly like this: 我的代码大致如下所示:

class Bottle{
 private boolean inUse=false;

 public synchronized void getBotttle(String name, int time)  {
    while(inUse) {
      try {
        System.out.println("The bottle is in use. You have to wait");
        wait();
      }
      catch (InterruptedException e) {}
    }

    System.out.println("Person "+name+" is using the bottle");
    inUse = true;
    notify(); //or notifyAll(), tried both

    try {
      Thread.sleep(time); // sleep the Thread with the person that is drinking at the moment for some time in order for him to finish
    }
    catch (InterruptedException e) {}
    System.out.println("The bottle is now free");
    inUse=false;
   // notify(); even if I put it here its still the same
 }
}

I just started using Threads in java so I wasn't sure where the notify() should really go. 我刚开始在Java中使用线程,所以不确定(notify()应该在哪里。 What is more I didn't understand that notify() releases the lock only after all the block that has the keyword synchronized is executed. 而且我不明白,notify()仅在执行了所有具有关键字synced的块之后才释放锁。 In my case that was not what I wanted and as happens when the lock is released the condition for the while method will be false and the printing will not be executed. 在我的情况下,这不是我想要的,并且在释放锁定时发生,while方法的条件将为false,并且不会执行打印。 The fact that the program was waiting properly and as intended made it hard for me to spot this. 程序正在正确等待并按预期进行的事实使我很难发现这一点。

And this is what I wanted and what I got working: 这就是我想要的,也是我正在工作的:

class Bottle{
 private boolean inUse=false;

 public void getBotttle(String name, int time)  {
    while(inUse) {
      try {
        System.out.println("The bottle is in use. You have to wait.");
        synchronized(this){
         wait();
        }
      }
      catch (InterruptedException e) {}
    }

    System.out.println("Person "+name+" is using the bottle");
    inUse = true;

    try {
      Thread.sleep(time); // sleep the Thread with the person that is drinking at the moment for some time in order for him to finish
    }
    catch (InterruptedException e) {}
    System.out.println("The bottle is free now.");
    inUse=false;
    synchronized(this){
     notifyAll();
    }
 }
}

Hopefully last edit: This should prevent 2 threads from skipping the while loop and should be the solution I was looking for 希望最后一次编辑:这应该防止2个线程跳过while循环,应该是我正在寻找的解决方案

class Bottle{
 private boolean inUse=false;

 public synchronized void getBotttle(String name, int time)  {
    while(inUse) {
      try {
        System.out.println("The bottle is in use. You have to wait.");
        wait();
      }
      catch (InterruptedException e) {}
    }

    System.out.println("Person "+name+" is using the bottle");
    inUse = true;
 }

 public synchronized void sleeping(String name, int time)
    try {
      Thread.sleep(time); // sleep the Thread with the person that is drinking at the moment for some time in order for him to finish
    }
    catch (InterruptedException e) {}
    notifyAll();
    System.out.println("The bottle is free now.");
    inUse=false;
 }
}

Edit: Guess not, printing that bottle is in use is not executed agian... 编辑:不要猜,打印不在使用中的瓶子...

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

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