[英]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.