java - 线程在 2 个队列中

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

最佳答案

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

我会做什么:向您的 Person 添加两个 boolean 标志:hasPumpedhasShopped - 一次人抽他们的汽油或商店,然后您将适当的标志设置为 true

BlockingQueues 替换你的 Queues (可能 LinkedBlockingQueue 在这里是合适的)——这是一个线程安全的队列,你可以调用take 以阻塞直到队列非空(而不是轮询然后在队列为空时 hibernate )。如果您更喜欢轮询和 hibernate ,那么您可能想要使用 ConcurrentLinkedQueue 代替,但我建议您在 BlockingQueue 上使用 take 代替。

Person 添加到GasPumpCashier 队列。当此人通过takepoll 从队列中移除时,然后检查其hasPumpedhasShopped 标志以确定如果需要任何额外的操作,例如,如果 Cashier 接受了这个人并且 hasPumped 为真,那么就没有必要询问 Person 他们是否想继续在加油队列中等待,因为他们已经完成抽气。

如果此人选择退出队列,则在适当的队列上调用 remove(person)

当此人完成加油后,如果他们的 hasShopped 标志为假,则将他们放入收银员队列中,同样,如果他们的 hasPumped 已完成购物,则将他们放入加油队列中 标志为假。

此实现不需要任何同步 block 或方法。

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
    }
}

关于java - 线程在 2 个队列中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18048703/

相关文章:

Java - 客户端的两个线程可以使用来自服务器的相同输入流吗?

java - Java 中的正则表达式转义序列错误

java - org.springframework.web.bind.ServletRequestBindingException 'binderId' 类不存在

java - 如何将播放器置于 libgDX 相机的中心

multithreading - TThread.Synchronize 在 Delphi 2009 中导致(接近)死锁(在 Delphi 7 中工作)

ios - 如何在应用程序在后台运行时监听锁定/解锁电话事件?

java - 用于文件下载操作的多线程代码,与单线程相比并不快

java - 未创建 slf4j/log4j 日志文件

java - 我需要什么背景?

java - 如何按文件记录顺序跟踪 Java 多线程输出