2013-08-04 3 views
1

Я пишу небольшую программу, которая поможет мне узнать многопоточность в java, и я зациклился на том, как реализовать какой-то сценарий.Тема в 2 очередях

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

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

Как я могу прыгать между этими двумя состояниями?

До сих пор у меня есть это:

Человек класс

public class Person implements Runnable { 

private GasPump pump; 
private Cashier cashier; 
... 
public void pumpGas() throws InterruptedException { 

    synchronized (this) { 
     pump.addCarToQueue(this); 
     wait(); 
    } 

    synchronized (pump) { 
     sleep((long) (Math.random() * 5000)); 
     pump.notify(); 
    } 
} 

public void buyCoffee() throws InterruptedException { 

    synchronized (this) { 
     cashier.addCustomerToQueue(this); // standing inline 
     wait(); 
    } 

    synchronized (cashier) { 
     sleep((long) (Math.random() * 5000)); // paying at cashier 
     cashier.notify(); 
    } 
} 
... 
} 

GasPump класс

public class GasPump implements Runnable { 

private Queue<Person> cars; 
... 
@Override 
public void run() { 
    while (gasStation.isOpen()) { 
     if (!cars.isEmpty()) { 
      Car firstCar = cars.poll(); 
      if (firstCar != null) { 
       synchronized (firstCar) { 
        firstCar.notifyAll(); 
       } 
      } else { 
       // ? 
      } 

      synchronized (this) { 
       try { 
        wait(); 
       } catch (InterruptedException e) { 
        e.printStackTrace(); 
       } 
      } 
     } 
    } 
} 
... 
} 

И класс кассир

public class Cashier implements Runnable { 

private Queue<Person> customers; 
... 
@Override 
public void run() { 
    while(coffeeHouse.isOpen()){ 
     if(!customers.isEmpty()){ 
      Car firstCustomer = customers.poll(); 
      if(firstCustomer != null){ 
       synchronized (firstCustomer) { 
        firstCustomer.notifyAll(); 
       } 
      } 

      synchronized (this) { 
       try { 
        wait(); 
       } catch (InterruptedException e) { 
        e.printStackTrace(); 
       } 
      } 
     } 
    } 
} 
... 
} 

ответ

3

Вы должны избегать использования wait и notify, так как их сложно использовать правильно и эффективно - вместо этого используйте классы от java.util.concurrent.

Что я хотел бы сделать: добавить два boolean флагов вашего Person: hasPumped и hasShopped - когда человек насосы своего газа или магазины, то вы установите соответствующий флаг true.

Замените ваш Queues с BlockingQueues (вероятно LinkedBlockingQueue уместно здесь) - это поточно-очереди, и вы можете вызвать take на него, чтобы блокировать до тех пор, пока очередь не непусто (а не опрос, а затем если очередь пуста). Если вы предпочитаете опрос и сон, тогда вы можете использовать вместо него ConcurrentLinkedQueue, хотя я советую вам использовать take вместо BlockingQueue.

Добавить Person в GasPump и Cashier очереди. Когда человек удаляется из очереди через take или poll, а затем проверить его hasPumped или hasShopped флаг, чтобы определить, есть ли какие-либо дополнительные действия необходимы, например, если Cashier берет человека и hasPumped верно, то нет никакой необходимости просить Person если они хотят продолжать ждать в газовой очереди, так как они уже закончили накачку.

Если человек хочет выйти из очереди, позвоните по номеру remove(person) в соответствующей очереди.

Когда человек заканчивает перекачку газа, затем помещает их в очередь кассира, если их флаг hasShopped является ложным, а также помещает их в газовую очередь, когда они закончили делать покупки, если их флаг hasPumped является ложным.

Вам не нужны никакие synchronized блоки или методы с этой реализацией.

public class Person implements Runnable { 

    private GasPump pump; 
    private Cashier cashier; 
    private boolean hasPumped, hasShopped, readyToPump, readyToShop; 
    private Thread thread; 

    public void run() { 
     thread = Thread.getCurrentThread(); 
     while(!hasPumped && !hasShopped) { 
      try { 
       readyToPump = false; 
       readyToShop = false; 
       if (!hasPumped) 
        pumpGas(); 
       if(!hasShopped) 
        buyCoffee(); 
       thread.sleep(FOREVER); 
      } catch (InterruptedException ex) { 
       // check flags to see what to do next 
      } 
     } 
    } 

    public void pumpGas() { 
     pump.addCarToQueue(this); 
    } 

    public void buyCoffee() { 
     cashier.addCustomerToQueue(this); 
    } 

    public void setReadyToPump() { 
     readyToPump = true; 
     thread.interrupt(); 
    } 

    public void setReadyToShop() { 
     readyToShop = true; 
     thread.interrupt(); 
    } 
} 

public class GasPump implements Runnable { 

    private BlockingQueue<Person> cars = new LinkedBlockingQueue<>(); 

    @Override 
    public void run() { 
     while (gasStation.isOpen()) { 
      Person person = cars.take(); 
      person.setReadyToPump(); 
     } 
     // clean up persons in queue 
    } 
} 

public class Cashier implements Runnable { 

    private BlockingQueue<Person> customers = new LinkedBlockingQueue(); 
    @Override 
    public void run() { 
     while(coffeeHouse.isOpen()){ 
      Person person = customers.take(); 
      person.setReadyToShop(); 
     } 
     // clean up persons in queue 
    } 
} 
+0

Должен ли я запускать поток Person после того, как я получу объект из take(), или если все потоки начнутся при вставке в очереди? –

+0

@ Yoni Levy Потоки 'GasPump',' Cashier' и 'Person' должны начинаться после их инициализации; «Человек» должен называть «pumpGas» и «buyCoffee» и «then», '' Person' должен 'спать' (' pumpGas' и 'buyCoffee' должны просто добавить' Person' в соответствующие очереди, они не должны звонить 'wait' или' sleep' или что-то в этом роде; потоки 'GasPump' и' Cashier' должны «прерывать» «Лицо» после его удаления из очереди, чтобы разбудить его, и «Лицо» должно затем проглотить/проигнорировать возникающее исключение. –

+0

@ Yoni Levy Я рекомендую вам использовать [ThreadPoolExecutor] (http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ThreadPoolExecutor.html), чтобы запустить ваши объекты Person; это приведет к тому, что все ваши объекты «Личность» будут иметь общий пул потоков, а не выделять отдельный поток для каждого «Человека» –

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