30. maj 2005 - 21:27 Der er 11 kommentarer og
1 løsning

Kan nogle se fejlen i dette program med (Thread)

Når jeg kører dette program får jeg følgende :

Thread-0 insætter 890 i queue

mypackage3.QueueException: QueueException on dequeue: Queue empty
    at mypackage3.QueueArrayBased.dequeue(QueueArrayBased.java:70)
    at mypackage3.Consumer.run(Consumer.java:14)
    at java.lang.Thread.run(Thread.java:534)

Thread-1 insætter 2277 i queue
Thread-2 insætter 2444 i queue

Hvorfor er min queue empty, når det tydeligvis fremgår at den først sætter ind i queuen, før exceptioenen kommer...

Mine klasser jeg arbejder med er:

public class ThreadsApp
{
  QueueArrayBased queue = QueueArrayBased.getInstance();
  Thread prod1, prod2, prod3; 
  Thread cons1, cons2, cons3;
 
  public ThreadsApp()
  {
    prod1 = new Thread(new Producer(queue));
    prod2 = new Thread(new Producer(queue)); 
    prod3 = new Thread(new Producer(queue));
 
    cons1 = new Thread(new Consumer(queue));
    //cons2 = new Consumer(queue);
    //cons3 = new Consumer(queue);
   
    //prod1.setPriority(7);
    prod1.start();
    prod2.start();
    prod3.start();
   
    //cons1.setPriority(2);
    cons1.start();
    //cons2.start();
    //cons3.start();
  }
  public static void main(String[] args)
  {
    ThreadsApp threadsApp = new ThreadsApp();
  }
}

.......................................................

public class Producer implements Runnable
{
  QueueArrayBased queue;
  TestObjekter tob;
 
  public Producer(QueueArrayBased queue)
  {
    this.queue = queue;
  }
  public void run()
  {
    int random = (int)(Math.random()*3000);
    tob = new TestObjekter(random);
    queue.enqueue(tob);
    System.out.println(Thread.currentThread().getName()+" insætter "+tob.getTal()+" i queue");
  }
}
.............................................................

public class Consumer implements Runnable
{
  QueueArrayBased queue;
  TestObjekter tob;
   
  public Consumer(QueueArrayBased queue)
  {
    this.queue = queue;
  }
  public void run()
  {
    tob = (TestObjekter)queue.dequeue();
    System.out.println(Thread.currentThread().getName()+" henter "+tob.getTal()+" fra queue");
  }
}
...........................................................

public class QueueArrayBased implements QueueInterface
{
  private  int MAX_QUEUE = 4; // maximum size of queue
  private Object[] items;
  private int front, back, count;
  private final static QueueArrayBased queue = new QueueArrayBased();
 
  public QueueArrayBased() {
    items = new Object[MAX_QUEUE]; 
    front = 0;
    back = MAX_QUEUE-1;
    count = 0;
  }  // end default constructor

  // queue operations:
  public boolean isEmpty() {
    return count == 0;
  }  // end isEmpty

  public boolean isFull() {
    return count == MAX_QUEUE;
  }  // end isFull
 
  public synchronized void enqueue(Object newItem) {
    if (!isFull())
    {
      back = (back+1) % (MAX_QUEUE);
      items[back] = newItem;
      ++count;
      notifyAll();
    }
    else
    {
    try
    {
      wait();
    }
    catch (InterruptedException e)
    {
      System.out.println(e);
    }
      throw new QueueException("QueueException on enqueue: "
                            + "Queue full");
    }  // end if
  }  // end enqueue

  public synchronized Object dequeue() throws QueueException {
  try
  {
    if (!isEmpty())
    {
      // queue is not empty; remove front
      Object queueFront = items[front];
      front = (front+1) % (MAX_QUEUE);
      --count;
      notifyAll();
      return queueFront;
    }
    else
    {
      wait();
    }
  }
  catch (InterruptedException e)
  {
    System.out.println(e);     
  }
    throw new QueueException("QueueException on dequeue: "+"Queue empty");



  public void dequeueAll()
  {
    items = new Object[MAX_QUEUE]; 
    front = 0;
    back = MAX_QUEUE-1;
    count = 0;
  }  // end dequeueAll

  public Object peek() throws QueueException {
    if (!isEmpty()) { 
      // queue is not empty; retrieve front
      return items[front];
    }
    else {
      throw new QueueException("Queue exception on peek: " +
                              "Queue empty");
    }  // end if
  }  // end peek
  public static synchronized QueueArrayBased getInstance()
  {
    return queue;
  }
} // end QueueArrayBased
Avatar billede arne_v Ekspert
30. maj 2005 - 21:32 #1
fordi empty queue exception udskrives efter udskrivning af enqueue så kan
du jo ikke konkludere at dequeue blev kaldt efter enqueue
Avatar billede arne_v Ekspert
30. maj 2005 - 21:33 #2
hvis du bruger java 1.5 er der funktionalitet til den slags indbygget i java
30. maj 2005 - 21:36 #3
jeg forstår..hvad du mener...men jeg har kørt programmet så tilpas mange gange nu, at sandsynligheden for at enqueue er blevet kaldt før dequeue,, bare én gang er meget stor....men det her er resultatet hver gang....
Avatar billede arne_v Ekspert
30. maj 2005 - 21:42 #4
jeg kan ikke helt følge din sandsyneligheds analyse
30. maj 2005 - 21:49 #5
Jeg mener at have læst et sted at du ikke i java kan bestemme hvilke tråde, der bliver afviklet først, med mindre men angiver priority...Jeg tænkte derfor at sandsyndligheden for at en tråd, der afvikler en enqueue før en dequeue vil forkomme på et aller andet tidspunkt...der er trods alt tre af dem frem for én dequeue.
Avatar billede arne_v Ekspert
30. maj 2005 - 21:54 #6
Der er ingen garanteret adfærd. Men derfor behøver det ikke være tilfældigt
jævnt fordelt.
30. maj 2005 - 22:26 #7
har sat dequeue til at sleepe lidt...virker fint nu


svar....
Avatar billede arne_v Ekspert
30. maj 2005 - 22:28 #8
svar
Avatar billede arne_v Ekspert
30. maj 2005 - 22:29 #9
og prøv så at kigge på Java 1.5 java.util.concurrent.ArrayBlockingQueue<E>
og java.util.concurrent.LinkedBlockingQueue<E>
30. maj 2005 - 22:34 #10
Ok..det vil jeg gøre...tak
Avatar billede snoop_one Nybegynder
31. maj 2005 - 08:42 #11
Nu skal det lige siges, at jeg ikke er tråd eksperten :) men jeg synes nu, at der er noget galt med opbygningen af din enqueue og dequeue.
F.eks. hvis din queue er tom eller fyldt vil dequeue og enqueue hhv. altid kaste en exception.
Fejlen er, at du måske venter på det forkerte tidspunkt. F.eks. i dit tilfælde, så bliver Consumer kørt først. Den går så ind i dequeue og finder ud af at listen er tom! Hvorefter den hopper ned til else og venter via wait() metoden. En Producer kommer ind og lægger et objekt i køen og kalder notifyAll(). Produceren vågner og den næste linie kode der bliver udført er en "throw new QueueException".
Så dit program gør faktisk som du har skrevet det ;).

Det du kunne gøre var i stedet - er noget ala:

public synchronized void enqueue(Object newItem) {
  if (!isFull()) {
      back = (back + 1) % (MAX_QUEUE);
      items[back] = newItem;
      ++count;
      notifyAll();
  } else {
      try {
        wait();
      } catch (InterruptedException e) {
        System.out.println(e);
      }
  } // end if
} // end enqueue

public synchronized Object dequeue() {
  try {
      while (isEmpty()) {
        wait();
      }
  } catch (InterruptedException e) {
      System.out.println(e);
  }  // queue is not empty; remove front
      Object queueFront = items[front];
      front = (front + 1) % (MAX_QUEUE);
      --count;
      notifyAll();
      return queueFront;
}

Prøv også at sætte navnet på dine tråede, således når du kalder getName() på dem at du så får deres navn
fremfor f.eks. Thread-X.
F.eks. vil:
 
  prod2 = new Thread(new Producer(queue));
  prod2.setName("prod2");
  prod2.start();

give:
  prod2 insætter 273 i queue
Avatar billede snoop_one Nybegynder
31. maj 2005 - 08:47 #12
ups det gik vist lidt for stærkt...
1. Se bort fra enqueue koden.
2. Det er selvfølgelig Consumer der vågner, efter Produceren har kaldt notifyAll().
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester