线程在2个队列中

时间:2013-08-04 23:36:39

标签: java multithreading

我正在编写一个小程序来帮助我在java中学习一些多线程,而且我仍然坚持如何实现某些场景。

该程序模拟了一个加油站,里面还有一个咖啡馆。 我希望能够创建以下场景:

  • 将人员添加到加油泵队列中。
  • 同时将一个人添加到咖啡馆的收银员队列中。
  • 如果泵队列中的人员在收银员队列转弯前到达,请允许他选择做什么(留在收银队列并退出泵队列或反过来)。

如何在这两种状态之间跳转?

到目前为止,我有这个:

Person类

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();
                }
            }
        }
    }
}
...
}

1 个答案:

答案 0 :(得分:3)

您应该避免使用waitnotify,因为很难正确有效地使用它们 - 请改用java.util.concurrent中的类。

我会做什么:在booleanPersonhasPumped添加两个hasShopped标记 - 一旦一个人抽出他们的汽油或商店,那么你设置适当的旗帜到true

将您的Queues替换为BlockingQueues(此处LinkedBlockingQueue可能是合适的) - 这是一个线程安全的队列,您可以按顺序调用take阻塞直到队列非空(而不是轮询,然后如果队列为空则休眠)。如果您更喜欢投票和睡觉,那么您可能希望使用ConcurrentLinkedQueue,但我建议您在take上使用BlockingQueue

Person添加到GasPumpCashier个队列。通过takepoll将此人从队列中删除后,请检查其hasPumpedhasShopped标记,以确定是否需要执行任何其他操作,例如{ {1}}取得此人并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
    }
}