簡體   English   中英

如何在可序列化 class 中正確使用“鎖定”變量進行同步?

[英]How to properly use a "lock" variable for synchronization in a Serializable class?

我試圖弄清楚如何在下面的代碼中避免 NPE。 我無法故意重現該錯誤,但每隔一段時間我就會在第 40 行synchronized(lock) {處得到一個 NPE。 我的猜測是它發生在序列化/反序列化過程之后 - 但這只是一個猜測。

我的 IDE 給了我一個編譯“提示”,上面寫着synchronization on a non-final variable (lock) ,但老實說,我對synchronized代碼塊以及可序列化的 class 如何影響final變量並不熟悉。

作為一個僅供參考,下面的代碼是從 Struts Wiki 復制/修改的: https://cwiki.apache.org/confluence/display/WW/HibernateAndSpringEnabledExecuteAndWaitInterceptor (朝向頁面底部的注釋)。

import com.opensymphony.xwork2.ActionInvocation;
import org.apache.struts2.interceptor.BackgroundProcess;
import org.springframework.orm.jpa.EntityManagerFactoryUtils;
import org.springframework.orm.jpa.EntityManagerHolder;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;

public class OpenSessionBackgroundProcess extends BackgroundProcess implements Serializable {

    private static final long serialVersionUID = 3884464561311686443L;

    private final transient EntityManagerFactory entityManagerFactory;

    // used for synchronization
    protected boolean initializationComplete;
    private transient Object lock = new Object();

    public OpenSessionBackgroundProcess(String name, ActionInvocation invocation, int threadPriority, EntityManagerFactory entityManagerFactory) {
        super(name, invocation, threadPriority);
        this.entityManagerFactory = entityManagerFactory;
        initializationComplete = true;
        synchronized (lock) {
            lock.notify();
        }
    }

    protected void beforeInvocation() throws Exception {
        while (!initializationComplete) {
            try {
                synchronized (lock) {  // <----- NPE HERE
                    lock.wait(100);
                }
            } catch (InterruptedException e) {
                // behavior ignores cause of re-awakening.
            }
        }
        EntityManager em = entityManagerFactory.createEntityManager();
        TransactionSynchronizationManager.bindResource(entityManagerFactory, new EntityManagerHolder(em));
        super.beforeInvocation();
    }

    protected void afterInvocation() throws Exception {
        super.afterInvocation();
        EntityManagerHolder emHolder = (EntityManagerHolder)
        TransactionSynchronizationManager.unbindResource(entityManagerFactory);
        EntityManagerFactoryUtils.closeEntityManager(emHolder.getEntityManager());
    }

    /**
     * Override default readObject() method when deserializing
     *
     * @param serialized the serialized object
     */
    private void readObject(ObjectInputStream serialized) throws IOException, ClassNotFoundException {
        serialized.defaultReadObject();
        lock = new Object();
    }
}

問題中的代碼和鏈接的 wiki 頁面存在無法解決的數據競爭條件。 不幸的是, BackgroundProcess的構造函數允許對this的引用通過啟動一個調用beforeInvocation的新線程來逃避構造函數 由於此調用可能發生在子 class 的構造函數完成之前,因此擴展BackgroundProcess是不安全的。

此代碼嘗試通過使用lock object、 initializationComplete標志和wait / notify用法來處理entityManagerFactory上的競爭條件。 但是,這只會將競爭條件從entityManagerFactory移動到lock ,因為 Java 僅在父 class 構造函數完成初始化子類的字段 無論該字段是內聯初始化還是在構造函數中初始化,這都是正確的。

您可以在對問題Java: reference escape的出色接受答案中找到有關此問題的更多詳細信息。

我最好的建議是避免使用BackgroundProcess並找到解決原始問題的另一種方法。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM