1

我正在编写一个小程序来帮助我学习 Java 中的一些多线程,但我一直坚持如何实现某些场景。

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

  • 将一个人添加到加油站队列中。
  • 同时在咖啡馆的收银队列中添加一个人。
  • 如果该人轮到抽水队列在轮到收银队列之前到达,允许他选择做什么(留在收银队列并退出抽水队列或相反)。

我怎样才能在这两种状态之间跳转?

到目前为止,我有这个:

人员类

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

气泵类

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

1 回答 1

3

您应该避免使用waitandnotify因为很难正确有效地使用它们 - 请改用 from 类java.util.concurrent

我会做什么:在你的 : 中添加两个标志-boolean一旦有人抽油或购物,那么你将适当的标志设置为.PersonhasPumpedhasShoppedtrue

替换你QueuesBlockingQueues(可能LinkedBlockingQueue这里合适) - 这是一个线程安全的队列,你可以调用take它以阻塞直到队列非空(而不是轮询然后在队列为空时休眠)。如果您更喜欢轮询和睡眠,那么您可能想要使用 aConcurrentLinkedQueue代替,尽管我建议您使用takeaBlockingQueue代替。

将 a 添加PersonGasPumpCashier队列。take当通过or将该人从队列中删除时poll,然后检查其hasPumpedorhasShopped标志以确定是否需要任何其他操作,例如,如果该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
    }
}
于 2013-08-05T00:09:40.903 回答