0

我的应用程序中有一个要求,其中“N”个产品可以与报价相关联。屏幕布局将有两个部分。顶部有一个包含报价相关信息的表格,底部用于容纳多个产品。我通过在底部放置 iframe 来实现此功能。单击按钮(使用 javascript)将添加/删除产品。每个 Product 窗口中显示的内容将由相同的 Action (ProductLinesAction.java)、JSP (ProductLines.jsp) 和其他相关资源呈现。这里的要点是,只要在屏幕上加载新的产品窗口,就会创建该 Action 类的多个实例。我在加载窗口时没有问题,因为它只是准备要显示的表单。虽然要保存报价,所有这些产品表单都将被提交,我合并的逻辑是 1 到 N-1 个动作实例将表单值放入一个 VO 中,该 VO 添加到 Vector 对象并保存在会话中(以便其他动作实例可以接受它从会话并在其上添加)。第 N 个动作实例旨在共同保存所有这些产品值。业务规则验证也在保存之前执行,因此第 N 个操作实例将与错误一起可用,这些错误应该显示在每个产品窗口中。第 N 个动作实例旨在共同保存所有这些产品值。业务规则验证也在保存之前执行,因此第 N 个操作实例将与错误一起可用,这些错误应该显示在每个产品窗口中。第 N 个动作实例旨在共同保存所有这些产品值。业务规则验证也在保存之前执行,因此第 N 个操作实例将与错误一起可用,这些错误应该显示在每个产品窗口中。

为了确保所有其他操作实例也能利用与其窗口对应的错误,我实现了等待和 notifyAll 机制,当尝试保存超过 6 个产品时,事情就会出错。代码如下。这段代码适用于小于或等于 6 的产品(我的意思是最多 6 个动作实例)。加载并保存第 7 个产品时,第 7 个实例在调试模式下根本不可见或可追溯(提交表单时实例未达到预期方法)。

任何人都可以对这里犯下的导致这个问题的错误有所了解。

public String submitProducts()
        throws Exception {

    String resultValue = "";
    /* Algorithm: */
    // 1. Read the Vector object from Session.
    // 2. Check whether the size of the Vector matches the Total Product windows count.
    // 3. If yes, call the Save operation and remove the list from session.
    // 4. If not, copy the values from current Action instance to VO.
    // 5. Add to List object and place in session.

    synchronized (productVOsInVector) {
        productVOsInVector = getProductVOVectorFromSession();
        if (productVOsInVector == null) {
            productVOsInVector = new Vector <ProductVO>();
        }
        log.info("Window Number is " + activeWindowNumber + ". List size is " + productVOsInVector.size());
        if (productVOsInVector.size() == (prodWindowCount - 1)) {
            productVOsInVector = mapActionToVO(productVOsInVector);
            resultValue = saveOperation(productVOsInVector);
            if (resultValue.equalsIgnoreCase(SUCCESS)) {
                session.put("OperationStatus", SUCCESS);
            }
            session.remove("productVOMapData");
        }
        else {
            if (quoteSaveStatus) {
                quoteSaveStatus = false;
            }
            session.put("OperationStatus", "");
            productVOsInVector = mapActionToVO(productVOsInVector);
            session.put("productVOMapData", productVOsInVector);
        }
        waitForOperationStatus();
    }
    System.out.println("Came out of sync block");
    System.out.println("Action Instance" + activeWindowNumber + " is resuming.");
    // Code to display the Error messages
    return resultValue;
}

public void waitForOperationStatus() {

    String opStatus = getOperationStatusFromSession();
    synchronized (productVOsInVector) {
        if (!opStatus.equalsIgnoreCase(SUCCESS)) {
            try {
                System.out.println("Window # " + activeWindowNumber + " Waiting");
                productVOsInVector.wait();                  
            }
            catch (InterruptedException e) {
                e.printStackTrace();
            }
            opStatus = getOperationStatusFromSession();
        }
        productVOsInVector.notifyAll();
    }
}
4

1 回答 1

5

以下问题本身已经是一个严重的问题:

synchronized (productVOsInVector) {
    productVOsInVector = getProductVOVectorFromSession();
    if (productVOsInVector == null) {
        productVOsInVector = new Vector <ProductVO>();
    }
    ...
}

您正在同步 引用的对象productVOsInVector,并立即将引用指向另一个对象。因此,下一个线程将在与第一个线程不同的对象上同步。

然后你在等待这个对象,并希望有人会通知你。

我没有分析更多,但你有一个严重的设计问题。首先,您不应该在 servlet 容器的线程之间进行同步。如果池中只有 6 个线程,并且它们都在等待第 7 个线程完成,那么就会出现死锁。如果池中有 12 个线程,并且有 2 个客户端同时执行此操作,那么也会出现死锁。即使你没有死锁,你也会让几个线程不可用,除了等待之外什么都不做,希望后续的 HTTP 请求会通知它们。如果最后一个请求由于某种原因没有出现(例如,用户关闭了他的浏览器,那么您有 6 个线程被永久阻塞。

因此,我的建议是:不要乱用线程。寻找另一种方式。

于 2012-09-25T12:36:28.433 回答