Java 保存集合的一部分,同时由其他线程添加新条目
我有一个批处理作业,它异步发送http请求并将它们收集到结果列表中。这很好用Java 保存集合的一部分,同时由其他线程添加新条目,java,concurrency,Java,Concurrency,我有一个批处理作业,它异步发送http请求并将它们收集到结果列表中。这很好用 public File addLine(CsvLine line) throws IOException { lines.add(line); return this; } 在尝试优化时,我希望将行存储到持久位置 public File addLine(CsvLine line) throws IOException { lines.add(line); if (lines.size(
public File addLine(CsvLine line) throws IOException {
lines.add(line);
return this;
}
在尝试优化时,我希望将行存储到持久位置
public File addLine(CsvLine line) throws IOException {
lines.add(line);
if (lines.size() > uploadService.getPartSize()) {
List<CsvLine> copy;
copy = new ArrayList<>(lines);
lines.removeAll(copy);
uploadService.save(copy);
}
return this;
}
public File addLine(CsvLine行)引发IOException{
行。添加(行);
if(lines.size()>uploadService.getPartSize()){
清单副本;
复制=新阵列列表(行);
行。删除所有(副本);
上传服务。保存(复制);
}
归还这个;
}
多个线程仍在添加到lines集合,但在制作副本时,我确保只删除要保存的行(copy collection)。
所以这不起作用,我试着在这个部分添加synchronized关键字
public File addLine(CsvLine line) throws IOException {
lines.add(line);
if (lines.size() > uploadService.getPartSize()) {
List<CsvLine> copy;
synchronized (this) {
copy = new ArrayList<>(lines);
lines.removeAll(copy);
}
uploadService.save(copy);
}
return this;
}
public File addLine(CsvLine行)引发IOException{
行。添加(行);
if(lines.size()>uploadService.getPartSize()){
清单副本;
已同步(此){
复制=新阵列列表(行);
行。删除所有(副本);
}
上传服务。保存(复制);
}
归还这个;
}
但是没有成功。我假设ArrayList的构造函数不是线程保存,如果我没有弄错的话,这也是唯一需要同步的部分
有人能指出我做错了什么吗?我创建了一个
双缓冲列表
对象来实现这一点
/**
* Lock free - thread-safe.
*
* Write from many threads - read with fewer threads.
*
* Write items of type T.
*
* Read items of type List<T>.
*
* @author OldCurmudgeon
* @param <T>
*/
public class DoubleBufferedList<T> {
/**
* Atomic reference so I can atomically swap it through.
*
* Mark = true means I am adding to it so momentarily unavailable for iteration.
*/
private final AtomicMarkableReference<List<T>> list = new AtomicMarkableReference<>(newList(), false);
// Factory method to create a new list - may be best to abstract this.
protected List<T> newList() {
return new ArrayList<>();
}
/**
* Get and replace the current list.
*
* Used by readers.
*
* @return List<T> of a number (possibly 0) of items of type T.
*/
public List<T> get() {
// Atomically grab and replace the list with an empty one.
List<T> empty = newList();
List<T> it;
// Replace an unmarked list with an empty one.
if (!list.compareAndSet(it = list.getReference(), empty, false, false)) {
// Failed to replace!
// It is probably marked as being appended to but may have been replaced by another thread.
// Return empty and come back again soon.
return Collections.<T>emptyList();
}
// Successfull replaced an unmarked list with an empty list!
return it;
}
/**
* Grab and lock the list in preparation for append.
*
* Used by add.
*/
private List<T> grab() {
List<T> it;
// We cannot fail so spin on get and mark.
while (!list.compareAndSet(it = list.getReference(), it, false, true)) {
// Spin on mark - waiting for another grabber to release (which it must).
}
return it;
}
/**
* Release the grabbed list.
*
* Opposite of grab.
*/
private void release(List<T> it) {
// Unmark it - should this be a compareAndSet(it, it, true, false)?
if (!list.attemptMark(it, false)) {
// Should never fail because once marked it will not be replaced.
throw new IllegalMonitorStateException("It changed while we were adding to it!");
}
}
/**
* Add an entry to the list.
*
* Used by writers.
*
* @param entry - The new entry to add.
*/
public void add(T entry) {
List<T> it = grab();
try {
// Successfully marked! Add my new entry.
it.add(entry);
} finally {
// Always release after a grab.
release(it);
}
}
/**
* Add many entries to the list.
*
* @param entries - The new entries to add.
*/
public void add(List<T> entries) {
List<T> it = grab();
try {
// Successfully marked! Add my new entries.
it.addAll(entries);
} finally {
// Always release after a grab.
release(it);
}
}
/**
* Add a number of entries.
*
* @param entries - The new entries to add.
*/
@SafeVarargs
public final void add(T... entries) {
// Make a list of them.
add(Arrays.<T>asList(entries));
}
}
同步对
行的所有访问(且仅限于此):
public File addLine(CsvLine行)引发IOException{
列表副本=空;
同步(行){
行。添加(行);
if(lines.size()>uploadService.getPartSize()){
复制=新阵列列表(行);
行。清除();
}
}
如果(复制!=null){
上传服务。保存(复制);
}
归还这个;
}
什么是行?现在如何处理同步?我现在处理它的方式是我那里的代码,我可以同步整个方法,但感觉不对。Lines是我存储结果的集合,我希望每x次刷新一行以控制内存消耗。结果包含10mil记录,甚至更多。你说这个方法被几个线程访问,但我在这里没有看到任何同步。为什么同步整个方法对您来说是错误的?我恐怕要回答我自己的问题,行是一个哈希集,而不是concurrenthashset,我的代码可能会抛出并发修改错误并吞没消息,因此代码似乎无法工作。我将对此进行更多的测试。你没有找到简单的解决方案,是吗?@AdamSkywalker-我找到了简单的解决方案,但如果它们不符合要求,我会放弃它们。但这里的要求是什么?我们不知道。使整个方法同步可能是一个解决方案。@jelle-在什么意义上错了?代码行太多?我删除了200多行测试代码以适应这里。
final String BYE = "BYE!";
public void test() throws InterruptedException {
final DoubleBufferedList<String> l = new DoubleBufferedList<>();
// Producer.
Thread p = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100000; i++) {
// Pump the queue.
l.add("" + i);
}
l.add(BYE);
}
});
// Consumer.
Thread c = new Thread(new Runnable() {
@Override
public void run() {
boolean done = false;
do {
List<String> got = l.get();
System.out.println("Got: " + got);
for (String s : got) {
if (s.equals(BYE)) {
done = true;
}
}
} while (!done);
}
});
// Fire it up.
p.start();
c.start();
// Wait for them to finish.
p.join();
c.join();
}
public File addLine(CsvLine line) throws IOException {
List<CsvLine> copy = null;
synchronized (lines) {
lines.add(line);
if (lines.size() > uploadService.getPartSize()) {
copy = new ArrayList<>(lines);
lines.clear();
}
}
if (copy != null) {
uploadService.save(copy);
}
return this;
}