2015-02-18 2 views
0

Так что я делаю программу для класса. Цель программы состоит в том, чтобы несколько человек съели фрукты из сумки. Плоды дают энергию, и энергия людей уменьшается каждую секунду. Проблема у меня в том, что каждый раз, когда плод съедают я делаю вывод, который выглядит следующим образом:Почему мой поток имеет одно и то же сообщение?

System.out.println("There are now " + fruitList.size() + " fruits left in the bag."); 

Это должно, насколько я понимаю, выходной текущий размер моей ArrayList. То, что я смущен, состоит в том, что если два человека (инициализированные через два разных потока) едят примерно в одно и то же время, это должно показаться, например, сначала: «В сумке осталось 10 фруктов», а затем «Там теперь осталось 9 фруктов в сумке ». Проблема в том, что оба этих сообщения покажут, что осталось 10 фруктов.

Это метод в классе Person, который несет ответственность за это действие:

public void eat(Fruit fruit, ArrayList<Fruit> fruitList, MagicBag magicBag) { 
    lock.lock(); 
    try { 
     while (getEnergy() > 20) { 
      newDeposit.await(); 
     } 
     if(energy < 30 && energy > 20) { 
      System.out.println(this.getName() + " is getting hungry."); 
     } else { 
     System.out.println(this.getName() + " picked a " + fruitList.get(fruitList.size() - 1).getName() + " and started to eat..."); 
     Thread.sleep(1000); 
     int fruitEnergy; 
     fruitEnergy = fruitList.get(fruitList.size() - 1).getEnergyValue(); 
     fruitList.remove(fruitList.size() - 1); 
     if(energy > 100) { 
      energy = 100; 
     } else { 
      energy += fruitEnergy; 
     } 
     System.out.println(this.getName() + " ate a " + fruitList.get(fruitList.size() - 1).getName() + "!"); 
     System.out.println("There are now " + fruitList.size() + " fruits left in the bag."); 
     magicBag.addFruitsToBag(fruitList, fruit); 
     newDeposit.signalAll(); 
     } 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } finally { 
      lock.unlock(); 
     } 
} 

И это то, что бег() делает в классе, который реализует Runnable, который работает под управлением выше код:

public void run() { 
    // TODO Auto-generated method stub 
    try { 
     while(running) { 
      if(fruitList.size() > 0) { 
      person.eat(fruit, fruitList, magicBag); 
      Thread.sleep(1000); 
      } 
      else { 
       kill(); 
      } 
     } 
    } catch(InterruptedException e) { 
     e.printStackTrace(); 
    } catch(IndexOutOfBoundsException ie) { 
     running = false; 
    } catch(NullPointerException ne) { 
     ne.printStackTrace(); 
    } 
} 

Так что, если бы кто-нибудь мог мне помочь, это было бы здорово. Если вам нужно что-то еще, просто спросите.

+0

Очень простое правило: если параллельный метод вызывает что-то вроде fruitlist.size() несколько раз, он делает это неправильно (потому что он может меняться между ними). Это не ваша проблема, а другая. В вашем случае вам просто нужно убедиться, что удаление будет первым, что вы делаете. И когда вы получаете доступ к списку (удалить/размер), вы должны синхронизировать его в течение этого короткого времени, поскольку список не является потокобезопасным. – eckes

ответ

3

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

+0

Да, это так. Первый раз работа с потоками так пропустил, что мне нужно было синхронизировать. Благодаря! – Asparatame

Смежные вопросы