场景:每秒调用一个线程数千次,以便对同一个表执行插入操作,并且当前正在逐个执行这些操作。
目标:定期进行批量插入以提高性能。
当调用线程的saveItem
方法时,尝试使用TimerTask将保存的对象添加到列表中,然后每隔2秒左右将它们组合成批量插入。
首先想到的是有两个列表,称之为toSave
和toSaveBackup
。当调用线程的saveItem
方法来保存它时,它将被添加到toSave列表中,但是一旦TimerTask启动并需要将所有内容保存到数据库,它将设置AtomicBoolean标志saveInProgress
为真正。此标志由saveItem
检查,如果saveInProgress
为真,它将添加到toSaveBackup而不是toSave。批量保存完成后,toSaveBackup中的所有项目都将移动到toSave列表,可能是列表中的同步块。
这是一种合理的方法吗?还是有更好的最佳做法?我的谷歌搜索技能让我失望,所以欢迎任何帮助。
其他信息:
更新:对CKing以下答案的调整达到了预期的方法:TimerTask每100毫秒运行一次并检查saveQueue
的大小以及自批量保存以来的时间长度。如果这些值中的任何一个超过配置的限制(每2秒或每1000条记录保存等),我们就会保存。 LinkedBlockingQueue用于简化同步。
再次感谢大家的帮助!
答案 0 :(得分:2)
看起来您的主要目标是等待预定义的时间,然后触发插入。当插入正在进行时,您不希望其他插入请求等待插入完成。插入完成后,您希望再次为下一个插入请求重复相同的过程。
我会在考虑到上述理解的情况下提出以下解决方案。您不需要有两个单独的列表来实现您的目标。另请注意,为了解释,我提出了一个老式的解决方案。我将介绍一些其他API,您可以在我的解释结束时使用它们。这是:
Timer
和TimerTask
。 ArrayList
,用于排队发送到saveItem
方法的插入请求。 saveItem
方法可以围绕此sycnrhonized
定义ArrayList
块。您可以在调用ArrayList
时向synchronized
块中的saveItem
添加项目。 TimerTask
在synchronized
方法的同一ArrayList
内应该有一个run
块。它应该将当前ArrayList
中存在的所有记录插入到数据库中。插入完成后,TimerTask
应该clear
ArrayList
并最终退出synchronized
块。当插入正在进行时,您将不再需要显式监视插入是否正在进行或创建ArrayList
的副本。在这种情况下,您的ArrayList
将成为共享资源。
如果您还希望size
成为继续插入的决定性因素,您可以这样做:
waitAttempts
中定义名为TimerTask
的int。此字段指示TimerTask
如果size
的{{1}}不够大,则list
不应执行任何操作的连续唤醒次数。TimerTask
醒来时,它都可以执行if(waitAttempts%3==0 || list.size > 10) { insert data } else { increment waitAttempts and do nothing. Exit the synchronized block and the run method }
之类的操作。您可以将3
和10
更改为适合您的吞吐量要求的任何数字。 注意使用内在锁定作为解释方法的手段。人们总是可以采用这种方法并使用现代结构(例如BlockingQueue
)来实现它,从而无需在synchronize
上手动ArrayList
。我还建议使用Executors.newSingleThreadScheduledExecutor()
而不是TimerTask
,因为它确保在任何给定时间只有一个线程运行,并且线程不会重叠。此外,waitAttempts
的逻辑是指示性的,需要进行调整才能正常工作。