7 Replies Latest reply: Jul 20, 2009 11:50 AM by 843789 RSS

    Concurenncy - wait(), notifyAll() methods

    843789
      Hello,

      I tried to make some kind of new example of wait/notifyAll methods but i met a problem. I want to make a machine which is creating bottles and placing them into chests in one thread, and another machine which is changing that chests. The first machine is waiting for the second machine while it's changing the chest. But unluckly, I do not understand why i can't use chest.notifyAll() from the place i used it. (it is in MachineChangingCheststs class in the end of run() method). Could some explain me it and tell how to make it working please?

      Here is the code:
      public class Main 
      { 
          public static void main (String str []) 
          { 
              Chest chest = new Chest();
              MachineCreatingBottles machine1 = new MachineCreatingBottles(chest);
              MachineChangingChests maszyna2 = new MachineChangingChests(chest);
              
              Thread production = new Thread(machine1, "production");
              Thread changer = new Thread(maszyna2, "changer");
              
              production.start();
              changer.start();
          }
      }
      /*Machine which is creating bottles in another thread. 
      This machine should wait after chest is full until the second machine complete their work.
       * */
      class MachineCreatingBottles implements Runnable
      {
          public MachineCreatingBottles(Chest chest)
          {
              this.chest = chest;
          }
          public void run() 
          {
             System.out.println(Thread.currentThread().getName()+" : I'm producing bottles");
             
                 synchronized(chest)
                 {
                      while(true)
                      {
                         try
                         {
                             chest.add(new Bottle());
                             System.out.println(Thread.currentThread().getName()+" : I have just produced "+(++i)+" bottle");
                             Thread.sleep(500);
                             if (i%10 == 0)
                             {
                                 System.out.println(Thread.currentThread().getName()+" : I inform that the chest is full");
                                 chest.wait();                    
                             }
                         }
                         catch(Exception e)
                         {
                            e.printStackTrace();
                         }
                     }
                }
          }
          
          private Chest chest;
          public int i = 0; // the number of how many bottles have been already produced.
      }
      class MachineChangingChests implements Runnable
      {
          public MachineChangingChests(Chest chest)
          {
              this.chest = chest;
          }
          public void run() 
          {
              while (true)
              {
                  synchronized(this)
                  {
                    while (!chest.isChestFull())
                    {
                       System.out.println(Thread.currentThread().getName()+" : I'm waiting for the full chest, actually the chest has got "+chest.listOfBottles.size()+" bottles");
                       try 
                       {
                           Thread.sleep(2000);
                       }
                       catch (InterruptedException ex) 
                       {
                          ex.printStackTrace();
                       }
                    }
                      
                    System.out.println(Thread.currentThread().getName()+" : I'm changing chests");
                    chest.listOfBottles.clear();
                    chest.notifyAll(); // why is that thing does not work here? how should i change it?
                  }
              }
          }
          
          private Chest chest;
      }
      class Chest
      {
          public boolean isChestFull()
          {
              if (listOfBottles.size() == CAPACITY)
                  return true;
              
              return false;
          }
      
          public void add(Object o)
          {
              if (!isChestFull())
               listOfBottles.add(o);
          }
          final int CAPACITY = 10;
          ArrayList listOfBottles = new ArrayList(CAPACITY);
      }
      class Bottle
      {
          
      }
      Best regards,

      Armon
        • 1. Re: Concurenncy - wait(), notifyAll() methods
          masijade
          Because you probably meant synchronized(chest) rather than synchronized(this).
          • 2. Re: Concurenncy - wait(), notifyAll() methods
            843789
            No i did synchronized(this) there instead of synchronized(chest) in order to be able to run both codes at the same time, if you change it to synchronized(chest)
            the output is:
            production : I'm producing bottles
            changer : I'm waiting for the full chest, actually the chest has got 0 bottles
            changer : I'm waiting for the full chest, actually the chest has got 0 bottles
            ....
            ....
            changer : I'm waiting for the full chest, actually the chest has got 0 bottles
            I keep up question pls.
            • 3. Re: Concurenncy - wait(), notifyAll() methods
              800298
              No he is right, you want to synchronize access to the chest so that only one thread can add bottles or switch chests at a time, but you need to move the sleep outside of the synchronized block to give the chest changer a chance to change chests.
                   while(true)
                   {
                         synchronized(chest)
                         {
                                 if (chestIsFull()) {
                                     System.out.println(Thread.currentThread().getName()+" : I inform that the chest is full");
                                 } else { 
                                     chest.add(new Bottle());
                                     System.out.println(Thread.currentThread().getName()+" : I have just produced "+(++i)+" bottle");
                                 }
                        }
              
                      Thread.sleep(500);
                  }
              Edited by: Edward_Kimber on 20.07.2009 12:30

              Edited by: Edward_Kimber on 20.07.2009 12:35
              • 4. Re: Concurenncy - wait(), notifyAll() methods
                masijade
                Edit: Nevermind, I didn't see you counter in the print statement (you need to shorten those statements, or stop do mulitple actions on the same line). In any case, why does not the "changer" thread simply wait (not sleep) until the "production" thread notifies it. Why are you "busy" waiting (which is what that "check, sleep, check" is, just not as busy as without the sleep).
                • 5. Re: Concurenncy - wait(), notifyAll() methods
                  843789
                  Okay,
                  I combined both of your ideas ;)

                  And i got something like that:
                  package buteleczka;
                  
                  import java.util.ArrayList;
                  
                  public class Main 
                  { 
                      public static void main (String str []) 
                      { 
                          Chest chest = new Chest();
                          Object lock = new Object();
                          
                          MachineCreatingBottles machine1 = new MachineCreatingBottles(chest);  
                          MachineChangingChests machine2 = new MachineChangingChests(chest);   
                          
                          Thread production = new Thread(machine1, "production");
                          Thread changer = new Thread(machine2, "changer");
                          
                          production.start();        
                          changer.start();
                      }
                  }
                  /*Machine which is creating bottles in another thread. 
                  This machine should wait after chest is full until the second machine complete their work.
                   * */
                  class MachineCreatingBottles implements Runnable
                  {
                      public MachineCreatingBottles(Chest chest)
                      {
                          this.chest = chest;
                      }
                      public void run() 
                      {
                          System.out.println(Thread.currentThread().getName()+" : I'm producing bottles");
                  
                          while(true)
                          {
                             try
                             {
                                 synchronized(chest)
                                 {
                                     System.out.println(Thread.currentThread().getName()+" : I have just produced "+(++i)+" bottle");
                                     chest.add(new Bottle());
                  
                                     if (i%10 == 0)
                                     {
                                         System.out.println(Thread.currentThread().getName()+" : I inform that the chest is full");
                                         chest.notifyAll();
                                     }
                                 }   
                                
                                Thread.sleep(500); // HERE why i have to use use thread.sleep ?
                             }
                             catch(Exception e)
                             {
                                e.printStackTrace();
                             }
                  
                         }
                      }
                      
                      private Chest chest;
                      public static int i = 0; // the number of how many bottles have been already produced.
                  }
                  class MachineChangingChests implements Runnable
                  {
                      public MachineChangingChests(Chest chest)
                      {
                          this.chest = chest;
                      }
                      public void run() 
                      {
                          while (true)
                          {
                                while (!chest.chestIsFull())
                                {
                                    synchronized(chest)
                                    {
                                       try 
                                       {
                                           chest.wait();
                  
                                           chest.amountOfBottles(chest);
                                           chest.change();
                                           chest.amountOfBottles(chest);
                                        
                                       }
                                       catch (InterruptedException ex) 
                                       {
                                           ex.printStackTrace();
                                       }
                                   }
                               }
                          }
                      }
                      private Chest chest;
                  }
                  class Chest
                  {
                      public synchronized boolean chestIsFull()
                      {
                          if (listOfBottles.size() == CAPACITY)
                              return true;
                          
                          return false;
                      }
                      public synchronized void add(Object o)
                      {
                          if (!chestIsFull())
                           listOfBottles.add(o);
                      }
                      public synchronized void change()
                      {
                          System.out.println(Thread.currentThread().getName()+" : I'm changing chests");
                          listOfBottles.clear();
                      }    
                      public synchronized int amountOfBottles(Chest chest)
                      {
                          System.out.println(Thread.currentThread().getName()+": Actually there is:"+listOfBottles.size()+" bottles in the chest");
                          return listOfBottles.size();
                      }        
                      final int CAPACITY = 10;
                      ArrayList listOfBottles = new ArrayList(10);
                  }
                  class Bottle
                  {
                      
                  }
                  But i noticed that I HAVE TO use Thread.sleep() in the first class (I commented that place) If i don't use Thread.sleep() there, mostly methods chest.amountOfBottles(chest);
                  chest.change();

                  chest.amountOfBottles(chest);
                  do not fire at all.

                  Why is that so? I suppose that i have to give time for changing chests, but what if somebody would have for example slower computer and it was different program. Mayby 100ms would be too small amount. And i thought sychronization would kill this problem.
                  • 6. Re: Concurenncy - wait(), notifyAll() methods
                    684401
                    You should never need to use Thread.sleep() unless you're actually trying to wait a specific amount of time. If you use it because the other process doesn't finish in time and you think it might take X amount of time, it's going to break somewhere down the line.

                    However, what's happening here is that you're getting the effect of a simulation. There's nothing stopping your thread from making bottle after bottle as fast as it can and adding it to the chest. Every time it starts making a bottle, it'll grab the synchronization lock on the chest. It's not that your other methods are never getting called -- more likely, you're never seeing another thread get control. The Thread.sleep() tells the JVM, "Well, I have 500 ms to wait before I can do anything, so you may as well let another thread run."

                    For synchronization to solve this problem, there has to be some condition for which the machine stops making bottles. Try this: Instead of making 10 bottles, then notifying, then waiting (during which the chest is emptied), then making 10 bottles again, have the bottlemaking machine wait() for the chest to be emptied.

                    Making Machine (Producer):
                    1) Make bottle
                    2) Add to chest
                    3) If chest is full, let consumers know they can empty the chest
                    4) Wait until chest is empty

                    Collecting Machine (Consumer):
                    1) Get bottle
                    2) If chest is empty, let Producer know we need more
                    3) Wait until chest is full again
                    • 7. Re: Concurenncy - wait(), notifyAll() methods
                      843789
                      Oh i see now, thanks for the help...

                      Well here is the last code:
                      class MachineCreatingBottles implements Runnable
                      {
                          public MachineCreatingBottles(Chest chest)
                          {
                              this.chest = chest;
                          }
                          public void run() 
                          {
                              System.out.println(Thread.currentThread().getName()+" : I'm producing bottles");
                              
                              while(true)
                              {
                                 try
                                 {
                                     synchronized(chest)
                                     {
                                         
                                         while (chest.chestIsFull())
                                         {
                                             System.out.println(Thread.currentThread().getName()+" : I inform that the chest is full");
                                             chest.wait();
                                         }
                                         
                                         System.out.println(Thread.currentThread().getName()+" : I have just produced "+(++i)+" bottle");
                                         chest.add(new Bottle());
                                         chest.notifyAll();
                                     }   
                                    
                                  // Thread.sleep(1000);
                                 }
                                 catch(Exception e)
                                 {
                                    e.printStackTrace();
                                 }
                             }
                          }
                          
                          private Chest chest;
                          public static int i = 0; // the number of how many bottles have been already produced.
                      }
                      class MachineChangingChests implements Runnable
                      {
                          public MachineChangingChests(Chest chest)
                          {
                              this.chest = chest;
                          }
                          public void run() 
                          {
                              while (true)
                              {
                               
                                   synchronized(chest)
                                   {
                                         while (!chest.chestIsFull())
                                         {
                                           try 
                                           {
                                              chest.wait();
                                             
                                           }
                                           catch (InterruptedException ex) 
                                           {
                                               ex.printStackTrace();
                                           }
                                         }
                                        
                                        chest.amountOfBottles(chest);
                                        chest.change();
                                        chest.amountOfBottles(chest);
                      
                                        chest.notifyAll();
                                   }
                              }
                          }
                          private Chest chest;
                      }
                      class Chest
                      {
                          public synchronized boolean chestIsFull()
                          {
                              if (listOfBottles.size() == CAPACITY)
                                  return true;
                              
                              return false;
                          }
                          public synchronized void add(Object o)
                          {
                              if (!chestIsFull())
                               listOfBottles.add(o);
                          }
                          public synchronized void change()
                          {
                              System.out.println(Thread.currentThread().getName()+" : I'm changing chests");
                              listOfBottles.clear();
                          }    
                          public synchronized int amountOfBottles(Chest chest)
                          {
                              System.out.println(Thread.currentThread().getName()+": Actually there is:"+listOfBottles.size()+" bottles in the chest");
                              return listOfBottles.size();
                          }        
                          final int CAPACITY = 10;
                          ArrayList listOfBottles = new ArrayList(10);
                      }
                      class Bottle
                      {
                          
                      }
                      Could you tell me if totally everything is fine now? And do you think that i was right making all methods in the Chest class synchronized?