Java 当向量对象上等待的线程大于6时,wait和notifyAll机制未按预期工作

Java 当向量对象上等待的线程大于6时,wait和notifyAll机制未按预期工作,java,multithreading,concurrency,thread-safety,Java,Multithreading,Concurrency,Thread Safety,我的应用程序中有一个要求,即“N”个产品可以与报价关联。屏幕布局将分为两部分。顶部有一个包含报价相关信息的表单,底部用来存放多个产品。我通过在底部使用iframe来实现这个功能。点击按钮(使用javascript)即可添加/删除产品。每个产品窗口中显示的内容将由相同的操作(ProductLinesAction.java)、JSP(ProductLines.JSP)和其他相关资源呈现。这里的要点是,每当在屏幕上加载新产品窗口时,就会创建该操作类的多个实例。我在加载窗口时没有问题,因为它只是准备要显

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

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

有谁能解释一下在这里犯下的错误,这是造成这个问题的原因

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();
    }
}
publicstringsubmitproducts()
抛出异常{
字符串resultValue=“”;
/*算法:*/
//1.从会话中读取向量对象。
//2.检查向量的大小是否与产品窗口总数匹配。
//3.如果是,请调用保存操作并从会话中删除列表。
//4.如果没有,则将当前操作实例中的值复制到VO。
//5.添加到列表对象并放置在会话中。
已同步(productVOsInVector){
productVOsInVector=getProductVOVectorFromSession();
if(productVOsInVector==null){
productVOsInVector=新向量();
}
log.info(“窗口编号为“+activeWindowNumber+”。列表大小为“+productVOsInVector.size());
if(productVOsInVector.size()==(prodWindowCount-1)){
productVOsInVector=mapActionToVO(productVOsInVector);
结果值=保存操作(productVOsInVector);
if(结果值相等信号案例(成功)){
会话put(“操作状态”,成功);
}
删除(“productVOMapData”);
}
否则{
如果(引用安全状态){
quoteSaveStatus=假;
}
session.put(“操作状态”);
productVOsInVector=mapActionToVO(productVOsInVector);
session.put(“productVOMapData”,productVOsInVector);
}
waitForOperationStatus();
}
System.out.println(“来自同步块”);
System.out.println(“操作实例”+activeWindowNumber+“正在恢复”);
//显示错误消息的代码
返回结果值;
}
公共无效waitForOperationStatus(){
字符串opStatus=getOperationStatusFromSession();
已同步(productVOsInVector){
if(!opStatus.equalsIgnoreCase(成功)){
试一试{
System.out.println(“窗口#“+activeWindowNumber+“等待”);
productVOsInVector.wait();
}
捕捉(中断异常e){
e、 printStackTrace();
}
opStatus=getOperationStatusFromSession();
}
productVOsInVector.notifyAll();
}
}

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

synchronized (productVOsInVector) {
    productVOsInVector = getProductVOVectorFromSession();
    if (productVOsInVector == null) {
        productVOsInVector = new Vector <ProductVO>();
    }
    ...
}
已同步(productVOsInVector){
productVOsInVector=getProductVOVectorFromSession();
if(productVOsInVector==null){
productVOsInVector=新向量();
}
...
}
您正在对
productVOsInVector
引用的对象进行同步,并立即将引用点指向另一个对象。因此,下一个线程将在与第一个线程不同的对象上同步

然后,您正在等待此对象,并希望有人会通知您

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


因此,我的建议是:不要弄乱线程。找到另一种方法。

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

synchronized (productVOsInVector) {
    productVOsInVector = getProductVOVectorFromSession();
    if (productVOsInVector == null) {
        productVOsInVector = new Vector <ProductVO>();
    }
    ...
}
已同步(productVOsInVector){
productVOsInVector=getProductVOVectorFromSession();
if(productVOsInVector==null){
productVOsInVector=新向量();