我有一个负责从MySQL数据库中清除旧数据的线程。我们批量删除所以如果有大的清除,则不会消耗db,因此线程执行清除,等待几秒钟,然后再次调用自身以继续清除(如果还有任何记录)。
我的问题是,我们允许人们为需要清除的内容设置多个规则。在一个没有批量执行任何操作的旧系统中,我们只需遍历每个“清除规则”并运行查询。
但是,现在我们有一个线程再次安排自己的系统,由于ArrayList
ConcurrentModificationException
删除规则
我有一个ArrayList<QueryParameters>
我们迭代。如果没有剩下的记录要清除,我们应该从列表中删除规则,以便下次运行它时不会重复它。
如何从列表中正确删除规则但不能获取CME?我想我可以使用ConcurrentHashMap,但我不想真正想存储一个key-&gt;值。
代码是一个更大的java应用程序的插件,我们正在使用他们的线程调度程序,只是FYI。
我尝试过两种方法来迭代arraylist,for循环和使用迭代器
public class PurgeTask implements Runnable {
private Prism plugin;
private ArrayList<QueryParameters> paramList;
private int total_records_affected = 0, cycle_rows_affected = 0;
private int purge_tick_delay;
/**
*
* @param plugin
*/
public PurgeTask( Prism plugin, ArrayList<QueryParameters> paramList, int purge_tick_delay ){
this.plugin = plugin;
this.paramList = paramList;
this.purge_tick_delay = purge_tick_delay;
}
/**
*
*/
public void run(){
if(paramList.size() > 0){
ActionsQuery aq = new ActionsQuery(plugin);
// Execute in batches so we don't tie up the db with one massive query
for (Iterator<QueryParameters> it = paramList.iterator(); it.hasNext(); ) {
QueryParameters param = it.next();
cycle_rows_affected = aq.delete(param);
plugin.debug("Purge cycle cleared " + cycle_rows_affected + " rows.");
total_records_affected += cycle_rows_affected;
// If nothing (or less than the limit) has been deleted this cycle, we need to move on
if( cycle_rows_affected == 0 || cycle_rows_affected < plugin.getConfig().getInt("prism.purge.records-per-batch") ){
// Log final count of cleared records
plugin.log("Cleared " + total_records_affected + " rows from the database. Using:" + param.getOriginalCommand() );
total_records_affected = 0;
// Remove the purge rule from the list so we don't repeat
paramList.remove(param);
} else {
// Items we're deleted. Leave params in queue and re-schedule this task
plugin.deleteTask = plugin.getServer().getScheduler().runTaskLaterAsynchronously(plugin, new PurgeTask( plugin, paramList, purge_tick_delay ), purge_tick_delay);
}
}
}
}
}
答案 0 :(得分:1)
ArrayList
由可能同时修改它的多个线程迭代。如果您不希望同时运行清除任务,则可以同步ArrayList
访问权限:
public void run {
synchronized(paramList) {
...
}
}
如果你需要并发,那么一个更好的数据结构就是CopyOnWriteArrayList,它可以保证列表的完整性,同时迭代它,但删除操作的成本更高。仍然比同步访问整个列表更有效。
此外,您需要使用List.remove()
方法删除参数:paramList(remove);
,CopyOnWriteArrayList
不支持迭代器操作。
此外,最好在PurgeTask
类中使用List
接口,而不是实现,更容易更改场景背后的数据结构。