3

我有一个线程负责从 MySQL 数据库中清除旧数据。我们分批删除,因此如果有大量清除,则不会消耗数据库,因此线程执行清除,等待几秒钟,然后再次调用自身以继续清除(如果还有任何记录)。

我的问题是我们允许人们为需要清除的内容设置多个规则。在一个不批量执行任何操作的旧系统中,我们只需遍历每个“清除规则”并运行查询。

但是,现在我们有了一个线程再次自行调度的系统,我可以从中删除规则ArrayList了,因为ConcurrentModificationException

我有一个ArrayList<QueryParameters>我们迭代的。如果没有要清除的记录,我们应该从列表中删除规则,以便下次线程运行时不会重复它。

如何正确地从列表中删除规则但无法获得 CME?我想我可以使用 ConcurrentHashMap 但我不想真正想要存储键-> 值。

该代码是一个更大的 java 应用程序的插件,我们正在使用他们的线程调度程序,仅供参考。

我尝试了两种方法来迭代数组列表、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);

                }
            }
        }
    }
}
4

1 回答 1

1

由可能同时修改它的ArrayList多个线程迭代。如果您不希望您的清除任务同时运行,您可以同步ArrayList访问:

public void run {
   synchronized(paramList) {
       ...
   }
}

如果您需要并发性,一个更好的数据结构将是CopyOnWriteArrayList,它可以在迭代列表时保证列表的完整性,但删除操作的成本更高。仍然比同步访问整个列表更有效。

此外,您需要使用List.remove()方法删除参数:paramList(remove);,不支持迭代器操作CopyOnWriteArrayList

此外,最好在您的PurgeTask类中使用List接口,而不是实现,这样更容易更改幕后的数据结构。

于 2013-03-28T22:00:56.263 回答