我试图使用从Set集合创建的parallelStream()的foreach方法将字符串插入StringBuffer。 问题是每次执行代码时,最终的String(StringBuffer.toString())的总数要少1个元素(每次尝试都为随机元素)。
我也将StringBuffer更改为StringBuilder,将parallelStream()更改为stream(),但总是少了1个元素。
我正在使用: -Java版本:java 1.8_121 -服务器:Weblogic 12.2.1.2(我认为这与问题无关) -Spring boot 2.0.2.RELEASE(我认为这与问题无关)
注意:我使用了Map来保存稍后应在进程中签名的pdf(在另一个HTTP请求中)。
Map<String, ClientPdf> dataToEncript = new HashMap<>(); // pdf name it will be the key for this map (it is unique in the sql query)
List<Client> listClients = // list of clients from database
Set<ClientPdf> clientsPdf = new HashSet<>();
for (Client client : listClients) {
clientsPdf.add(client.clientPdf()); // clientPdf() generate a new object ClientPdf, which is similar to Client class, but with less fields (essential for the Set)
}
log.debug("Generating documents");
clientsPdf.parallelStream().forEach(pdf -> {
// some code to generate pdf
log.debug("Inserting pdf: {}", pdf); // this log print, for example, 27.000 lines
dataToEncript.put(pdf.getPdfName(), pdf);
});
StringBuffer sb = new StringBuffer(); // StringBuffer or StringBuilder, the same problem
for (ClientPdf clientPdf : dataToEncript.values()) {
sb.append(clientPdf.getPdfName() + ";" + clientPdf.getRut() + "\n"); // appending all values of de map dataToEncript, it will append 26.669 (1 less)
}
答案 0 :(得分:4)
clientsPdf.parallelStream().forEach(pdf -> {
// ...
dataToEncript.put(pdf.getPdfName(), pdf);
});
dataToEncript
并非线程安全的数据结构,因此这可能会导致荒谬和奇怪的错误,如您正在观察的错误
通常,使用forEach
通常是一个不好的信号,并且您应该几乎总是使用Collector
或其他方法。例如,在这里您应该使用
clientsPdf.parallelStream()
.collect(Collectors.toConcurrentMap(Pdf::getPdfName, pdf -> pdf));
获取正确的地图。
更好的是,你可以写
clientsPdf.parallelStream()
.map(clientPdf -> clientPdf.getPdfName() + ";" + clientPdf.getRut() + "\n")
.collect(Collectors.joining())
获得最终的String
,而无需手动管理StringBuffer
等。
答案 1 :(得分:0)
因为HashMap
如上Wasserman所述不是线程安全的。
如果多个线程正在访问同一对象并尝试修改其结构,则可能导致HashMap
状态的不一致。
因此,引入了HashTable
,SynchronizedMap
或ConcurrentHashMap
以在多线程环境(例如HashMap
)中使用parallelStream()
。
您可以按如下所示简单地重写代码的第一行:
Map<String, ClientPdf> dataToEncript = Collections.synchronizedMap(new HashMap<>());
现在,应该重新运行程序后才能获得正确的结果。
顺便说一句,HashTable
和SynchronizedMap
的性能都不好,您可以使用ConcurrentHashMap
来解决此问题。
祝你好运!
答案 2 :(得分:-1)
尝试使用数组而不是hashMap。我在与地图不同的上下文中遇到了类似的问题。因此,您应该使用stringToEncrypt和clientPdfToEncrypt或类似的东西来替换dataToEncrypt。 无论如何,您还可以对dataToEncrypt,listClients和clientsPdf进行调试,以准确确定错误发生的位置。