производитель-потребитель с использованием синхронизации

Я написал код для реализации проблемы «производитель-потребитель», и, похоже, он работает нормально без необходимости синхронизации. Возможно ли это?

Как протестировать код и проверить, работает ли он на самом деле правильно или нет? Как узнать, произойдет ли взаимоблокировка? Прямо сейчас я не выхожу из циклов (то есть производитель продолжает вставлять, а потребитель продолжает потреблять в бесконечном цикле). Я использую циклическую очередь размера 3 (ради простоты) в качестве общего ресурса.

Вот мой код:

import java.util.*;

public class PCImpl implements Runnable 
{
Thread t;
QforPC qObj;

 public static void main(String[] args)
 {
     QforPC qObject=new QforPC();

     //These create 2 different objects! Each object has it's own thread of execution.
     //Synchronization is needed when 2 threads use the same object
    new PCImpl("Producer",qObject);
    new PCImpl("Consumer",qObject);
 }

 PCImpl(String name,QforPC qObj)
 {
     this.qObj=qObj;
     t=new Thread(this,name);
     t.start();
 }

 public void run()
 {
         if(Thread.currentThread().getName().equals("Producer"))
         {
             while(true)
             {
                  Random rgen=new Random();
                  int n=rgen.nextInt(100);
                  if(n!=0)
                              qObj.Producer(n);
                         try
                    {
                       Thread.sleep(200);
                     }
                      catch(InterruptedException e)
                    {

                    }
               }

            }


         if(Thread.currentThread().getName().equals("Consumer"))
         {
             while(true)
                  {
                 try
               {
                 Thread.sleep(1500);
               }
                catch(InterruptedException e)
               {
                  }
              qObj.Consumer();

              }
         }

  }
}



public class QforPC 
{
int[] q={0,0,0};
int r=0,f=0;
  public void Producer(int item)
     {

         if(r!=q.length && canProducer())
         {
             q[r]=item;
             System.out.println("The item inserted into the queue is:"+ item);
             r++;
         }
         if(r==q.length && f>0)
             r=0;
         else if(r==q.length && f==q.length)
         {
             r=0;
             f=0;
         }
     }

     public void Consumer()
     {
         int item;
         System.out.println("The value of isQueue empty is:"+ isEmpty());

         if(f!=q.length && isEmpty()==false)
         {
             System.out.println("Entered the consumer method");
             item=q[f];
             System.out.println("The item fetched from the queue is:"+item);
             q[f]=0;
             f++;
         }
         if(f==q.length && r<f)
             f=0;

     }

     public boolean isEmpty()
     {
         for(int k=0;k<q.length;k++)
         {
             if(q[k]==0 && k==q.length-1)
                 return true;

         }
         return false;
     }

     public boolean canProducer()
     {
         for(int k=0;k<q.length;k++)
         {
                 if(q[k]==0)
                 return true;

         }
         return false;
     }
} 

person collegian    schedule 14.12.2010    source источник
comment
Вероятно, это испортится, если вы удалите Sleeps.   -  person SwDevMan81    schedule 14.12.2010
comment
Добро пожаловать в природу ошибок синхронизации. Кажется, что он работает нормально до тех пор, пока полностью не взорвется.   -  person Mark Peters    schedule 14.12.2010
comment
Почему бы не разделить логику производителя и потребителя на две отдельные реализации Runnable? Этот дизайн очень некрасивый и приведет к тому, что код будет полностью непригодным для сопровождения.   -  person Mark Peters    schedule 14.12.2010
comment
@Mark- Значит ли это, что он не будет работать без синхронизации? Кроме того, спасибо за предложение!   -  person collegian    schedule 14.12.2010
comment
Печально то, что ваши ассистенты, вероятно, понятия не имеют, как правильно протестировать код и/или указать, в чем проблема. По крайней мере, если они такие же, как мои...   -  person Karl Knechtel    schedule 14.12.2010


Ответы (4)


То, что вы пытались сделать, это реализовать синхронизацию с использованием ожидания занятости. В псевдокоде вы в основном делаете следующее:

Producer()
{
   if (buffer.hasemptyspaces())
   {
      produce(buffer);
   }
   else
   {
      sleep(n);
   }
}

Consumer()
{
   if (buffer.hasfullspaces())
   {
      consume(buffer);
   }
   else
   {
      sleep(n);
   }
}

Ваш код будет работать нормально, пока производитель и потребитель одновременно не попытаются выполнить продукцию () и потребление (). Другими словами, любое из этих событий может происходить не очень часто, но определенно возможно и обязательно произойдет!

В Java ConcurrentLinkedQueue реализует алгоритм без ожидания для общего буфера. Я уверен, что это другие реализации, если вы посмотрите вокруг.

person Sanjit Saluja    schedule 14.12.2010
comment
Значит, в моей реализации без синхронизации работать не может и в какой-то момент программа вылетит? - person collegian; 14.12.2010
comment
да. При реализации потоков и общих данных полезно подумать, есть ли способ чередовать ваши потоки таким образом, что они будут аварийно завершать работу, планировщик найдет его. - person Sanjit Saluja; 14.12.2010

Нет такого понятия, как the Producer-Consumer problem. Производитель-Потребитель — это шаблон проектирования, который может быть или не быть эффективной реализацией решения проблемы, а не проблемой сам по себе.

Я УВЕРЕН, что существует множество реализаций производитель-потребитель, которые не требуют синхронизации. Это полностью зависит от того, чего вы пытаетесь достичь и какие данные вы производите/потребляете.

Кроме того, у вас должна быть проблема, которую нужно решить, если вы хотите сказать, что ваша реализация работает без синхронизации. Работает над чем? Я понятия не имею, что ты делаешь.

person Falmarri    schedule 14.12.2010
comment
Я просто пытаюсь производить, а затем потреблять! - person collegian; 14.12.2010
comment
Забавно, но статья в Википедии о проблеме «производитель-потребитель», кажется, не согласна. - person Mark Peters; 14.12.2010
comment
Похоже, мое определение проблемы не совпадает с их. Думаю, именно поэтому я инженер, а не компьютерный ученый. - person Falmarri; 14.12.2010

Это можно сделать с помощью очереди без блокировок, но не так, я рекомендую вам прочитать Java Concurrency in Practice. Если к вашему коду одновременно обращаются несколько потоков, у вас будет много ошибок, у вас проблемы с публикацией и синхронизацией! Но, как сказал Фарлмарри, это зависит от использования этого кода.

person DVD    schedule 14.12.2010

на самом деле вы не решаете проблему производителя/потребителя, а просто обходите ее :) Ваш код работает из-за времени и из-за того факта, что если один из двух потоков не может поместить/получить ресурс, который он запрашивает, он в основном спит некоторое время и пытается снова. Хотя это работает (когда вам не нужно сразу обрабатывать событие), это тратит процессорное время.

Вот почему семафоры настоятельно рекомендуются для решения такого рода проблем, как вы можете прочитать здесь.

http://en.wikipedia.org/wiki/Producer-consumer_problem

до свидания

person Andrea Sindico    schedule 14.12.2010
comment
На самом деле, похоже, что реализация OP очень похожа на реализацию, указанную в этой статье как неадекватную. - person Mark Peters; 14.12.2010