安全地将对象列表传递给重新安排自己的线程

时间:2013-03-28 21:49:06

标签: java

我有一个负责从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);

                }
            }
        }
    }
}

1 个答案:

答案 0 :(得分:1)

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

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

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

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

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