如何从数据库加载并重置Web应用程序中的配置映射

时间:2013-12-20 17:25:28

标签: java multithreading web-applications locking servletcontextlistener

我需要从数据库加载配置映射。配置映射表示为

   Hashtable<String, Hashtable<String,String>>

我们无法在开始时加载映射,因为我们依赖于另一个war应用程序来进行数据库连接。因此,映射是在第一次调用实现ResourcesStorage的类(单例)ServletContextListener中的配置时加载的。同时,重置JSP在此侦听器中调用静态reset()方法。我使用reentrant锁添加了同步。我只锁定重置,因为不同的线程应该能够同时检索数据。但是,这不起作用。

我正在

IllegalMonitorStateException  on notFull.await();   
java.lang.IllegalMonitorStateException
        at java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:127)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1175)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer.fullyRelease(AbstractQueuedSynchronizer.java:1606)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:1922)

我的设计是否正确?

import java.util.Hashtable;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class ResourcesStorage implements ServletContextListener {

private static ResourcesStorage instance;
protected static Log log = LogFactory.getLog(ResourcesStorage.class);
protected Hashtable<String, Hashtable<String,String>> DBsettings =new Hashtable    <String, Hashtable<String,String>>();
private ServletContext context = null;
private boolean isLoading = false;

 public synchronized boolean isLoading() {
    return isLoading;
}

public  synchronized void setLoading(boolean isLoading) {
    this.isLoading = isLoading;
}

private Lock lock = new ReentrantLock();
private Condition notFull = lock.newCondition();

public void contextDestroyed(ServletContextEvent arg0) {}

public void contextInitialized(ServletContextEvent event) {

    this.context = event.getServletContext();
        //initialize the static reference _instance
    instance=this;
    reloadAllSettings();
}

public  Hashtable<String, Hashtable<String,String>> getDBSettings()
{
    return DBsettings;
}
public static String getSettings(String groupName, String keyName)
{
    ResourcesStorage instance = ResourcesStorage.getInstance();
    Hashtable<String, Hashtable<String,String>> dbsettings = instance.getDBSettings();
    Hashtable<String,String> group =(Hashtable<String,String>) dbsettings.get(groupName);
    if(group!=null && !group.isEmpty())
        return group.get(keyName);
    else
        return null;
}

public String getValueSettings (String groupName, String keyName)
{
    try 
    {
        while (isLoading() == false)
        {
             notFull.await();
        }
        Hashtable<String,String> group =(Hashtable<String,String>) DBsettings.get(groupName);
        if(group!=null && !group.isEmpty())
        {
            return group.get(keyName);
        }
        else
        {
            return null;
        }
    }
    catch (Exception e)
    {
        log.error("getValueSetting", e);    
    }
    return null;
}

public static void reSet(){
    ResourcesStorage instance = ResourcesStorage.getInstance();
    instance.reloadAllSettings();
}

public void reloadAllSettings(){
    lock.lock();
    setLoading(true);
    try
    {
// .....            
//getting resources from the database 
        setLoading(false);
        notFull.signal();
    } catch (Exception e) {
//...
    }
    finally
    {
        lock.unlock();
    }
}

public static ResourcesStorage getInstance() 
{
    return instance;
}

public ServletContext getContext() {
    return context;
}

public void setContext(ServletContext context) {
    this.context = context;
}
}

2 个答案:

答案 0 :(得分:1)

在多个线程访问(公共)配置数据的情况下,您可以从AtomicReference中受益,这些数据可以在任何给定时刻刷新。刷新通常由一个线程(在某个事件或特定时间段之后)发生,但如果多个线程可以同时刷新,则需要一些额外的锁定(请参阅下面的代码示例)。

获取配置数据的线程必须在必要时重新使用返回的配置数据实例,以防止将旧配置数据与新配置数据混合。这通常意味着使用配置数据和使用配置数据的方法创建单独的类。但无论如何你都需要使用AtomicReference。

以下代码示例应该让您了解如何使用AtomicReference:

import java.util.HashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

public class Q20708889 {

    private static final Object reloadLock = new Object();
    private static final AtomicInteger reloadCount = new AtomicInteger();

    private AtomicReference<DbSettings> dbSettingsRef = new AtomicReference<DbSettings>();

    public DbSettings getDbSettings() {

        DbSettings dbs = dbSettingsRef.get();
        if (dbs == null) {
            dbs = reload();
        }
        return dbs;
    }

    public DbSettings reload() {

        DbSettings dbs = null;
        int rld = reloadCount.get();
        synchronized (reloadLock) {
            if (rld < reloadCount.get()) {
                // Reload was already done
                dbs = dbSettingsRef.get();
            } else {
                reloadCount.incrementAndGet();
                dbs = new DbSettings();
                dbs.load();
                dbSettingsRef.set(dbs);
            }
        }
        return dbs;
    }

    class DbSettings {

        private HashMap<String, HashMap<String, String>> theSettings;

        public void load() {

            theSettings = new HashMap<String, HashMap<String,String>>();
            // getting resources from the database
        }

        public String getValueSettings(String groupName, String keyName) {

            String value = null;
            HashMap<String, String> group = theSettings.get(groupName);
            if (group != null && !group.isEmpty()) {
                value = group.get(keyName);
            }
            return value;
        }
    }

}

答案 1 :(得分:0)

建议:

设置已加载或未加载。如果已加载,请使用它们。

如果尚未加载,请调用synchronized方法。这是您唯一需要的锁定机制。在方法中检查它们是否已被加载(由于竞争条件),如果有,则返回。否则,当你阻止正在等待的其他线程时,去获取它们。

确保将设置引用保存在易变变量中。

编辑:

我的建议不支持让多个线程同时加载相同的数据。这是故意的。