Почему этот многопоточный код зависает - PullRequest
4 голосов
/ 09 декабря 2011

В приведенном ниже коде, когда я выполняю класс producercon, иногда выполнение зависает, выглядит как тупик.Но если я синхронизирую get_flag (), то таких проблем нет.

Я не могу понять, как может быть проблема.flag может иметь значение true или false, поэтому в оператор if попадет только один из producer или consumer.После того, как один из них войдет в if, он войдет в монитор с объектом r (оба инициализируются с одинаковой ссылкой на объект).Единственная проблема, которая может произойти, это то, что объект r изменяется при вызове функции increment_decrement (), и get_flag () читает флаг одновременно, но даже тогда он не введет if в этой итерации,но он войдет в блок if на следующей итерации, и даже если первый поток не покинул монитор, он будет ждать его там (до блока synchronized).

Как ипочему программа останавливается / зависает, если get_flag () не сделан synchronized?

import java.io.*;

class resource
{
   private boolean res, flag;

   resource ()
   {
     flag=false;
   }

   boolean get_flag ()
   {
     return flag;
   }

   void increment_decrement (String s,boolean t)
   {
     res=t;
     flag=t;
      try 
      {
        System.out.print("\n"+s+":"+res);
        Thread.sleep(200);
      }
      catch(InterruptedException e)
      {
      }
   }
}

class producer implements Runnable
{
    resource r1;
    Thread t1;

    producer(resource r)
    {
      r1 = r;
      t1 = new Thread(this);
      t1.start();
    }

    public void run ()
    {  
      while (true)
      {
        if(r1.get_flag () == false)
        {
          synchronized(r1)
          {
            r1.increment_decrement("Producer",true);
          }
        }
      }
    }

   public void waitForThread () throws InterruptedException
   {
     t1.join ();
   }
}

class consumer implements Runnable
{
   resource r2;
   Thread t2;

   consumer(resource r)
   {
     r2 = r;
     t2 = new Thread (this);
     t2.start();
   }

   public void run()
   {
     while (true)
     {
       if(r2.get_flag () == true)
       {
         synchronized(r2)
         {
           r2.increment_decrement("Consumer",false);
         }
       }
     }
   }

   public void waitForThread () throws InterruptedException
   {
     t2.join ();
   }
} 

public class producercon
{
   public static void main(String args[])
   {
     try
     {
        System.out.print("PRESS CTRL+C TO TERMINATE\n");

        resource r = new resource();
        consumer c = new consumer(r);
        producer p = new producer(r);

        c.waitForThread ();
        p.waitForThread ();
     }
     catch(InterruptedException e)
     {
     }
   }
}

Ответы [ 3 ]

6 голосов
/ 09 декабря 2011

Ваш вызов get_flag () не является ни потокобезопасным, ни энергозависимым. Это означает, что в кэше потока 1 это может быть true, в то время как в кэше потока 2 это может быть false.

4 голосов
/ 09 декабря 2011

Вам нужно сделать логическое значение либо volatile, либо AtomicBoolean.Сейчас несколько потоков пытаются получить доступ к логическому значению, которое никоим образом не синхронизировано.

0 голосов
/ 09 декабря 2011

Эта реализация производителя / потребителя довольно странная.

Ни производитель, ни потребитель не ждут, пока ресурс окажется в адекватном состоянии, и доступ к ресурсу недостаточно защищен (флаг должен быть защищен некоторой блокировкой, чтобы обеспечить его видимость между потоками).

Один из способов улучшить этот дизайн - использовать стандартную систему ожидания / уведомления. Другим способом было бы использовать семафор в ресурсе, чтобы гарантировать, что только один поток может получить доступ к ресурсу за один раз. Наконец, вы можете использовать высокоуровневую конструкцию, такую ​​как java.util.concurrent.SynchronousQueue, для передачи некоторых данных непосредственно от производителя к потребителю.

...