保留Java中Timer.schedule的调用堆栈。

huangapple go评论160阅读模式
英文:

Preserve call stack of, Timer.schedule in java

问题

我有一个守护线程正在运行,当厨师不忙并且有订单需要交付时,会调用一个函数(prepareOrder)。prepareOrder函数在一定时间间隔后调用orderComplete函数,时间间隔取决于完成订单所需的时间。现在我面临的问题是,只有对prepareOrder的最后一次调用会显示在标准输出上。

守护线程:

package ui;
import Model.takeOrderModel;
public class daemonThread extends Thread{
    //在主驱动函数的主方法中调用此方法
    private takeOrderModel orderModel;
    daemonThread(takeOrderModel orderModel){
        this.orderModel = orderModel;
    }
    public void assignCook(){
        while(true){
            int toComplete = orderModel.toCompleteOrders.size();
            if (!orderModel.cookBusy && toComplete > 0) orderModel.prepareOrder();
        }
    }
}

prepareOrder函数:

public void prepareOrder(){
    // 从列表中选择最后一个元素
    if (toCompleteOrders.size() > 0){
        String nextPrepare = toCompleteOrders.get(toCompleteOrders.size() - 1);
        order orderToComplete = allOrdersPlaced.get(nextPrepare);
        completeOrder(orderToComplete);
        toCompleteOrders.remove(nextPrepare);
    }
}

// 将订单从toComplete移动到prepared的辅助函数
private void completeOrder(order orderToComplete){
    changeCookState();
    new java.util.Timer().schedule(
        new java.util.TimerTask(){
            @Override
            public void run() {
                changeCookState();
                preparedOrders.add(orderToComplete.id);
                deliverOrder(orderToComplete.id);
            }
        }, (long) (orderToComplete.timeToComplete * 60)
    );
}

public void changeCookState(){
    this.cookBusy = !cookBusy;
}

// 从prepared列表中移除订单并将其放入delivered列表
public String deliverOrder(String completedOrder){
    preparedOrders.remove(completedOrder);
    deliveredOrders.add(completedOrder);
    System.out.println(String.format("The order of %s is here", allOrdersPlaced.get(completedOrder).customerName));
    return String.format("The order of %s is here", allOrdersPlaced.get(completedOrder).customerName);
}

主驱动函数的代码:

orderMachine.takeNewOrder(fullMeal, "Tom");
orderMachine.takeNewOrder(halfMeal, "Bob");
daemonThread backThread = new daemonThread(orderMachine);
backThread.setDaemon(true);
backThread.assignCook();

现在对我来说,只有最后一个下单的订单("Bob")会打印在标准输出上。如何使由Timer.schedule创建的所有调用保留在堆栈中。

(编辑部分略)

英文:

I have a daemon thread running which calls a function (prepareOrder) whenever the cook is not busy and there are orders to be delivered. The prepareOrder calls the orderComplete function after a certain interval of time depending upon the time required to complete the order. Now the problem i am facing is only the last call to the prepareOrder gets displayed on sout.

The daemon

package ui;
import Model.takeOrderModel;
public class daemonThread extends Thread{
    //call this method in the main method of driving fucntion
    private  takeOrderModel orderModel;
    daemonThread(takeOrderModel orderModel){
        this.orderModel = orderModel;
    }
    public void assignCook(){
        while(true){
            int toComplete = orderModel.toCompleteOrders.size();
            if ( !orderModel.cookBusy && toComplete>0 ) orderModel.prepareOrder();
        }
    }
}

The prepare order function.

 public void prepareOrder(){
    // pick the last element from list
    if (toCompleteOrders.size() > 0){
        String nextPrepare = toCompleteOrders.get(toCompleteOrders.size()-1);
        order orderToComplete = allOrdersPlaced.get(nextPrepare);
        completeOrder(orderToComplete);
        toCompleteOrders.remove(nextPrepare);
        }
    }

    //Helper function to prepareOrder moves an order from toComplete to prepared order
    private void completeOrder(order orderToComplete){
        changeCookState();
        new java.util.Timer().schedule(
                new java.util.TimerTask(){
                    @Override
                    public void run() {
                        changeCookState();
                        preparedOrders.add(orderToComplete.id);
                        deliverOrder(orderToComplete.id);
                    }
                }, (long) (orderToComplete.timeToComplete*60)
        );
    }

    public void changeCookState(){
        this.cookBusy = !cookBusy;
    }

    // MODIFIES removes a order from the prepared list and puts it in delivered list
    public String deliverOrder(String completedOrder){
        preparedOrders.remove(completedOrder);
        deliveredOrders.add(completedOrder);
        System.out.println(String.format("The order of %s is here", allOrdersPlaced.get(completedOrder).customerName));
        return String.format("The order of %s is here", allOrdersPlaced.get(completedOrder).customerName);
    }

The main function driving code.

orderMachine.takeNewOrder(fullMeal, "Tom");
orderMachine.takeNewOrder(halfMeal, "Bob");
daemonThread backThread = new daemonThread(orderMachine);
backThread.setDaemon(true);
backThread.assignCook();

Now for me only the last placed order("Bob") gets printed on sout. How can all calls created by Timer.schedule stay in stack.


Edits

The take new order function.

public boolean takeNewOrder(List<item> itemsInOrder, String customerName){
        try {
            order newOrder = new order(itemsInOrder, customerName);
            allOrdersPlaced.put(newOrder.id, newOrder);
            toCompleteOrders.add(newOrder.id);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

Edit 2

here is the public repo containing the complete code
https://github.com/oreanroy/Share_code_samples/tree/master/takeOrder

答案1

得分: 1

在这段代码中的问题是一个并发性错误 - cookBusy 变量被两个不同的线程写入。要修复这个问题,可以使用 AtomicBoolean 而不是 boolean,因为它是线程安全的。

AtomicBoolean cookBusy = new AtomicBoolean(false);

使用 compareAndSet 来确保在更新之前将共享变量设置为已知值。

public void changeCookState(boolean busy) {
    if (!this.cookBusy.compareAndSet(!busy, busy)) {
        throw new RuntimeException("共享变量被设置为意外的值");
    }
}
英文:

The problem in this code is a concurrency bug - the cookBusy variable is being written to from two different threads. To fix this, use an AtomicBoolean instead of a boolean, as this is thread safe.

AtomicBoolean cookBusy = new AtomicBoolean(false);

Use compareAndSet to ensure the shared variable is set to a known value before updating it.

    public void changeCookState(boolean busy){
        if (!this.cookBusy.compareAndSet(!busy, busy))
        {
            throw new RuntimeException("shared variable set to unexpected value");
        }
    }

huangapple
  • 本文由 发表于 2020年4月9日 12:57:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/61114266.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定