我需要从数据库加载配置映射。配置映射表示为
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;
}
}
答案 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方法。这是您唯一需要的锁定机制。在方法中检查它们是否已被加载(由于竞争条件),如果有,则返回。否则,当你阻止正在等待的其他线程时,去获取它们。
确保将设置引用保存在易变变量中。
编辑:
我的建议不支持让多个线程同时加载相同的数据。这是故意的。