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

标签 java multithreading web-applications locking servletcontextlistener

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

   Hashtable<String, Hashtable<String,String>>

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

我正在获取

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;
}
}

最佳答案

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

获取配置数据的线程必须在必要时重新使用返回的配置数据实例,以防止旧配置数据与新配置数据混合。这通常意味着使用配置数据和使用配置数据的方法创建一个单独的类。但无论如何,您都需要它来使用 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;
        }
    }

}

关于java - 如何从数据库加载并重置 Web 应用程序中的配置映射,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20708889/

相关文章:

java - java中文件的状态

java - 如何在 Spring 的一个 ConstraintValidator 中同时验证 @PathVariable 和 @RequestBody?

c# - 如何获取主线程并设置其优先级

c# - 异步socket,接收字符串消息

java - 在聚合类中使用克隆的最佳实践

java - 为什么我必须使用 "this"关键字进行前向引用?

Java线程访问对象

java - Windows Hello API

java - session 对象的父级 : request or application

javascript - 网络书阅读器分页算法