Java 8 并行流重复项

Java 8 并行流重复项,java-8,java-stream,Java 8,Java Stream,我正在从数据库中检索大块数据,并使用这些数据将其写入其他地方。为了避免长时间的处理,我尝试使用并行流来编写它当我将其作为顺序流运行时,它工作得非常好。但是,如果我将其更改为并行,则其行为很奇怪:它会多次打印同一对象(超过10次)。 @PostConstruct public void retrieveAllTypeRecords()引发SQLException{ info(“检索批量类型记录”); 试一试{ Stream typequeryastream=jdbcStream.gettypequ

我正在从数据库中检索大块数据,并使用这些数据将其写入其他地方。为了避免长时间的处理,我尝试使用并行流来编写它
当我将其作为顺序流运行时,它工作得非常好。但是,如果我将其更改为并行,则其行为很奇怪:它会多次打印同一对象(超过10次)。

@PostConstruct
public void retrieveAllTypeRecords()引发SQLException{
info(“检索批量类型记录”);
试一试{
Stream typequeryastream=jdbcStream.gettypequeryastream();
typequeryastream.forEach((类型)->{
logger.info(“打印类型为field1:{}和field2:{}.”,Type.getField1(),Type.getField2());//同一个对象在这里被打印多次
//将此对象写入其他位置
});
logger.info(“已完成类型数据的完全检索”);
}捕获(例外e){
记录器错误(“错误:+e”);
}
}
公共流getTypeQueryAsStream()引发SQLException{
String sql=typeRepository.getQueryAllTypesRecords();//检索字符串格式的sql查询
TypeMapper TypeMapper=新的TypeMapper();
JdbcStream.StreamableQuery query=JdbcStream.StreamableQuery(sql);
Stream=query.Stream()
.map(行->{
返回typeMapper.mapRow(row);//将列值映射到对象值
});
回流;
}
公共类StreamableQuery实现了Closeable{
(...)
public Stream()引发SQLException{
final SqlRowSet rowSet=new resultsetrappingsqlrowset(preparedStatement.executeQuery());
final SqlRow SqlRow=新的SqlRowAdapter(行集);
Supplier=()->spliterator.spliterator未知大小(新迭代器(){
@凌驾
公共布尔hasNext(){
return!rowSet.isLast();
}
@凌驾
公共SqlRow next(){
如果(!rowSet.next()){
抛出新的NoTouchElementException();
}
返回sqlRow;
}
},Spliterator.CONCURRENT);
return StreamSupport.stream(supplier,Spliterator.CONCURRENT,true);//此布尔值将流设置为并行
}
}
我也尝试过使用
typequeryastream.parallel().forEach((type)
,但结果是一样的

输出示例:
[ForkJoinPool.commonPool-worker-1]信息类型服务-使用字段1:L6797和字段2:P1433保存类型。
[ForkJoinPool.commonPool-worker-1]信息类型服务-使用字段1:L6797和字段2:P1433保存类型。
[main]信息类型服务-使用字段1:L6797和字段2:P1433保存类型。

[ForkJoinPool.commonPool-worker-1]信息类型服务-使用字段1:L6797和字段2:P1433保存类型。

好吧,看看你的代码

    final SqlRow sqlRow = new SqlRowAdapter(rowSet);

    Supplier<Spliterator<SqlRow>> supplier = () -> Spliterators.spliteratorUnknownSize(new Iterator<SqlRow>() {
…
        @Override
        public SqlRow next() {
            if (!rowSet.next()) {
                throw new NoSuchElementException();
            }
            return sqlRow;
        }
    }, Spliterator.CONCURRENT);
请注意,对于许多用例,如本例,实现
拆分器
比实现
迭代器
(无论如何都需要通过
Spliterator unknownsize
进行包装)更简单。此外,不需要将此实例化封装到
供应商

最后一点,当前的实现对于大小未知的流没有很好的表现,因为它将
Long.MAX_VALUE
视为一个非常大的数字,忽略了“未知”规范赋予它的语义。提供估计大小对并行性能非常有益,它不需要精确,事实上,在当前的实现中,即使是一个完全虚构的数字,比如
1000
,也可能比正确使用
Long.MAX_VALUE
来表示整个大小不详

    final SqlRow sqlRow = new SqlRowAdapter(rowSet);

    Supplier<Spliterator<SqlRow>> supplier = () -> Spliterators.spliteratorUnknownSize(new Iterator<SqlRow>() {
…
        @Override
        public SqlRow next() {
            if (!rowSet.next()) {
                throw new NoSuchElementException();
            }
            return sqlRow;
        }
    }, Spliterator.CONCURRENT);
public Stream<TypeRecord> stream(TypeMapper typeMapper) throws SQLException {
    SqlRowSet rowSet = new ResultSetWrappingSqlRowSet(preparedStatement.executeQuery());
    SqlRow sqlRow = new SqlRowAdapter(rowSet);

    Spliterator<TypeRecord> sp = new Spliterators.AbstractSpliterator<TypeRecord>(
            Long.MAX_VALUE, Spliterator.CONCURRENT|Spliterator.ORDERED) {
        @Override
        public boolean tryAdvance(Consumer<? super TypeRecord> action) {
            if(!rowSet.next()) return false;
            action.accept(typeMapper.mapRow(sqlRow));
            return true;
        }
    };
    return StreamSupport.stream(sp, true); //this boolean sets the stream as parallel
}