[英]HashMap and ArrayList adding while iterating/looping
我有一个游戏,每X秒它会将内存中的更改值写回我的数据库。 当编辑它们保存的数据时,这些值存储在容器(HashMaps和ArrayLists)中。
为简单起见,假设我只有一个容器写入数据库:
public static HashMap<String, String> dbEntitiesDeletesBacklog = new HashMap<String, String>();
我的DB编写循环:
Timer dbUpdateJob = new Timer();
dbUpdateJob.schedule(new TimerTask() {
public void run() {
long startTime = System.nanoTime();
boolean updateEntitiesTableSuccess = UpdateEntitiesTable();
if (!updateEntitiesTableSuccess){
try {
conn.rollback();
} catch (SQLException e) {
e.printStackTrace();
logger.fatal(e.getMessage());
System.exit(1);
}
} else { //everything saved to DB - commit time
try {
conn.commit();
} catch (SQLException e) {
e.printStackTrace();
logger.fatal(e.getMessage());
System.exit(1);
}
}
logger.debug("Time to save to DB: " + (System.nanoTime() - startTime) / 1000000 + " milliseconds");
}
}, 0, 10000); //TODO:: figure out the perfect saving delay
我的更新方法:
private boolean UpdateEntitiesTable() {
Iterator<Entry<String, String>> it = dbEntitiesDeletesBacklog.entrySet().iterator();
while (it.hasNext()) {
Entry<String, String> pairs = it.next();
String tmpEntityId = pairs.getKey();
int deletedSuccess = UPDATE("DELETE" +
" FROM " + DB_NAME + ".entities" +
" WHERE entity_id=(?)", new String[]{tmpEntityId});
if (deletedSuccess != 1) {
logger.error("Entity " + tmpEntityId + " was unable to be deleted.");
return false;
}
it.remove();
dbEntitiesDeletesBacklog.remove(tmpEntityId);
}
我是否需要为dbEntitiesDeletesBacklog HashMap和其他未包含在此摘录中的容器“保存到数据库”时创建某种锁定机制? 我认为我需要,因为它创建了它的迭代器,然后循环。 如果在创建迭代器之后以及在完成循环之前添加了某些内容,该怎么办? 对不起,这更像是一个流程问题,而不是代码帮助问题(因为我包含了很多示例代码),但我想确保很容易理解我想要做什么和询问。
我使用的其他容器的问题如下:
public static ArrayList<String> dbCharacterDeletesBacklog = new ArrayList<String>();
private boolean DeleteCharactersFromDB() {
for (String deleteWho : dbCharacterDeletesBacklog){
int deleteSuccess = MyDBSyncher.UPDATE("DELETE FROM " + DB_NAME + ".characters" +
" WHERE name=(?)",
new String[]{deleteWho});
if (deleteSuccess != 1) {
logger.error("Character(deleteSuccess): " + deleteSuccess);
return false;
}
}
dbCharacterDeletesBacklog.clear();
return true;
}
非常感谢,一如既往地为此提供任何帮助。 非常感谢!!
如果您同时访问地图,则至少需要同步地图(通过Collections.synchronizedMap
),否则您可能会遇到非确定性行为。
除此之外,正如您所建议的那样,您还需要在迭代期间锁定地图。 从Collections.synchronizedMap()
的javadoc,建议是:
当迭代任何集合视图时,用户必须手动同步返回的地图:
Map m = Collections.synchronizedMap(new HashMap());
...
Set s = m.keySet(); // Needn't be in synchronized block
...
synchronized(m) { // Synchronizing on m, not s!
Iterator i = s.iterator(); // Must be in synchronized block
while (i.hasNext())
foo(i.next());
}
不遵循此建议可能会导致非确定性行为。
或者 ,使用ConcurrentHashMap
而不是常规HashMap来避免在迭代期间需要同步。 对于游戏来说,这可能是一个更好的选择,因为您可以避免长时间锁定您的收藏。
可能更好 ,考虑轮换新的集合,这样每次更新数据库时,您都会获取集合并将其替换为写入所有新更新的新空集合,从而避免在数据库写入发生时锁定集合。 在这种情况下,集合将由某个容器管理,以允许此抓取和替换为线程安全。 <<<注意:在这种情况下,您不能将底层集合暴露给修改代码,因为您需要保持其引用严格私有以使交换有效(并且不引入任何竞争条件)。
以下是我将要使用的示例。 我在这里张贴,希望能帮助其他有类似问题的人。
public class MyDBSyncher {
public static boolean running = false;
public static HashMap<String, String> dbEntitiesInsertsBacklog_A = new HashMap<String, String>();
public static HashMap<String, String> dbEntitiesInsertsBacklog_B = new HashMap<String, String>();
public MyDBSyncher(){
Timer dbUpdateJob = new Timer();
dbUpdateJob.schedule(new TimerTask() {
public void run() {
running = true;
boolean updateEntitiesTableSuccess = UpdateEntitiesTable();
running = false;
}
}, 0, 10000); //TODO:: figure out the perfect saving delay
}
public HashMap getInsertableEntitiesHashMap(){
if (running){
return dbEntitiesInsertsBacklog_B;
} else {
return dbEntitiesInsertsBacklog_A;
}
}
private boolean UpdateEntitiesTable() {
Iterator<Entry<String, String>> it2 = getInsertableEntitiesHashMap().entrySet().iterator();
while (it2.hasNext()) {
Entry<String, String> pairs = it2.next();
String tmpEntityId = pairs.getKey();
//some DB updates here
it2.remove();
getInsertableEntitiesHashMap().remove(tmpEntityId);
}
return true;
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.