Java 安全地将对象列表传递给重新调度自身的线程

Java 安全地将对象列表传递给重新调度自身的线程,java,Java,我有一个线程负责从MySQL数据库中清除旧数据。我们分批删除数据库,以便在有大量清除时不会消耗数据库,因此线程执行清除,等待几秒钟,然后再次调用自身以继续清除(如果还有任何记录) 我的问题是,我们允许人们为需要清除的内容设置多个规则。在一个没有成批执行任何操作的旧系统中,我们只需迭代每个“清除规则”并运行查询 然而,现在我们有了一个系统,线程再次调度自己,我可以从ArrayList中删除规则,因为ConcurrentModificationException 我有一个我们迭代的ArrayList

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

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

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

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

如何从列表中正确删除规则,但不获取CME?我想我可以使用ConcurrentHashMap,但我不想存储一个key->value

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

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

                }
            }
        }
    }
}
public类PurgeTask实现可运行{
私有棱镜插件;
私有ArrayList参数列表;
私有整数总计记录受影响=0,周期行受影响=0;
私人整数清除(勾选)延迟;;
/**
* 
*@param插件
*/
公共PurgetTask(Prism插件、ArrayList参数列表、int purge_tick_delay){
this.plugin=plugin;
this.paramList=paramList;
this.purge\u tick\u delay=purge\u tick\u delay;
}
/**
* 
*/
公开募捐{
如果(paramList.size()>0){
ActionsQuery-aq=新的ActionsQuery(插件);
//分批执行,这样我们就不会将数据库与一个大规模查询捆绑在一起
for(Iterator it=paramList.Iterator();it.hasNext();){
QueryParameters param=it.next();
循环行受影响=aq.delete(参数);
调试(“清除循环清除”+循环受影响的行数+“行数”);
受影响的总记录数+=受影响的循环行数;
//如果本周期没有删除任何内容(或低于限制),我们需要继续前进
if(cycle_rows_impact==0 | | cycle_rows_impact
多个线程会对
ArrayList
进行迭代,这些线程可能会同时修改它。如果不希望清除任务同时运行,可以同步
ArrayList
访问:

public void run {
   synchronized(paramList) {
       ...
   }
}
如果您需要并发性,那么更好的数据结构应该是在迭代列表时保证列表的完整性,但是删除操作的成本更高。仍然比同步访问整个列表更有效

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


此外,最好在
PurgeTask
class a
列表中使用,而不是在实现中使用,这样更容易在幕后更改数据结构。

如何迭代
ArrayList
?你能发布代码吗?将代码添加到帖子中你试过什么了吗?同步结构?添加同步方法来修改列表?呃,我缺少一些简单的东西。谢谢我已经使用它更新了代码。remove()并且它在我的测试中运行得非常好,但是一个尝试新构建的用户看到了以下异常:-我刚刚为他运行了一个完整的jar重建,以防万一,但是,使用此it.remove方法是否仍有可能获得CME错误?我选择了CopyOnWrite解决方案,因为这仅适用于真正不经常运行的清除规则,因此效率不是一个大问题。根据用户设置,可能最多有10个周期(如果有的话),并且它仅在用户每次启动应用程序时运行,通常每周运行几次。谢谢