Java 排序时出现非常奇怪的NullPointerException

Java 排序时出现非常奇怪的NullPointerException,java,multithreading,executorservice,Java,Multithreading,Executorservice,我有一个空指针异常,因为列表adPics中有一些空值。这种情况很少发生。怎么可能呢 (此代码并行下载图像并在本地保存。) 列出下载的图像(列出图像,最终字符串项文件夹){ 最终列表adPics=new ArrayList(); final executor服务executor=newFixedThreadPool(20); forEach成像仪( picUrl->executor.submit(()->{ 试一试{ 字符串imageNewFileName=imagesUrls.indexOf(p

我有一个空指针异常,因为列表
adPics
中有一些空值。这种情况很少发生。怎么可能呢

(此代码并行下载图像并在本地保存。)

列出下载的图像(列出图像,最终字符串项文件夹){
最终列表adPics=new ArrayList();
final executor服务executor=newFixedThreadPool(20);
forEach成像仪(
picUrl->executor.submit(()->{
试一试{
字符串imageNewFileName=imagesUrls.indexOf(picUrl)+“+”getExtension(picUrl);
字符串bigPicUrl=picUrl.replace(“b.jpg”、“ab.jpg”);//大版本
copyURLToFile(新URL(bigPicUrl),新文件(itemFolder,imageNewFileName),10,10);
adPics.add(imageNewFileName);
}捕获(IOEX异常){
log.log(Level.WARNING,“无法下载映像{0}({1})”,新对象[]{picUrl,例如.getMessage()});
}
}));
executor.shutdown();
试一试{
执行器等待终止(15L,毫秒);
}捕获(中断异常例外){
log.log(Level.WARNING,“无法等待所有图像下载”);
}
Collections.sort(adPics);//列表中的空值将导致此处的NPE。如何存在空值?
返回adPics;
}

有时
adPics
列表具有
null
值。这就是NPE的原因。但是怎么做呢?分析线程中执行的代码时,不能添加
null
值。如果下载映像时出现问题,它将引发IOException
imageNewFileName
不能为
null


这段代码是Java 8,它使用Apache Commons IO lib。

该方法不会停止正在运行的线程。它只等待所有线程完成或达到
超时时间
。因此,您的线程仍在向列表中添加项目

你也应该考虑下载和复制到文件系统,即使超时也在运行。

一个简单但并不完美的解决方案是在达到超时时设置一个标志,并在添加更多项目之前检查该标志


更好的方法是在达到超时后中断线程。这还应该包括中断下载和文件复制。

您的代码有几个问题。保罗已经指出了一个问题。另一个问题是您同时访问列表ADPIC

要在不使用同步列表的情况下解决问题,您可以在调用结束时创建一个列表[CompletableFuture]。 每个CompletableFuture都应返回图像文件名,但在尝试下载图像之前,它应检查时间,以确定是否要启动该操作。如果没有,您可以使用空值完成该CompletableFuture。
在CompletableFuture.allOf中,您可以创建一个不带空值的新列表并对该列表进行排序。

(关于问题,而不是下行投票)我想这是因为您是从多个线程添加到列表中的,但列表没有正确同步。当您这样做时,可能会发生有趣的事情。您确定
等待终止
调用没有超时,并且您在同时向其中添加元素时开始排序
adPics
?不知道为什么会抛出一个NPE,但是
ArrayList
不是线程安全的,所以我想任何事情都可能发生。@LuísSoares你不会的。只有当前线程被中断时,您才会看到该消息。请注意,您没有检查
的返回值,因此您不知道它是否超时。如果在
==null
上设置条件断点,则很容易确定是否超时。请复制实际文本并将其粘贴到问题中,而不是发布文本图像。图像不能参与搜索结果,它们对视力受损的用户毫无用处,而且它们比本地浏览器文本更不清晰。非常有趣。最干净的方法是什么?如果有人评论并发添加是个问题,又该怎么办?这是两个问题的混合吗?它们是独立的吗?如果你使用的是同步列表,你会以某种方式掩饰你的问题。它应该避免NPE。但在后台,下载、复制和添加仍处于活动状态。在最后,你的列表将不再被确定排序,因为排序后仍然可以添加新项目。所以。。中断下载/复制的最佳方式是什么?这有意义吗?或者。。。你的意思是我应该在添加到列表之前检查经过的时间吗?@sstan sure从多个线程访问列表应该是同步的:)使用同步列表不会解决问题,它只会掩盖根本问题并导致其他问题。例如,最终的列表不能确定排序。我同意,这就是为什么我给出了一个不使用同步列表解决它的想法。
List<String> downloadAdImages(List<String> imagesUrls, final String itemFolder) {
       final List adPics = new ArrayList<>();
       final ExecutorService executor = newFixedThreadPool(20);
       imagesUrls.forEach(
               picUrl -> executor.submit(() -> {
                   try {
                       String imageNewFileName = imagesUrls.indexOf(picUrl) + "." + getExtension(picUrl);
                       String bigPicUrl = picUrl.replace("b.jpg", "ab.jpg"); // big version
                       copyURLToFile(new URL(bigPicUrl), new File(itemFolder, imageNewFileName), 10, 10);
                       adPics.add(imageNewFileName);
                   } catch (IOException ex) {
                       log.log(Level.WARNING, "Could not download image {0} ({1})", new Object[]{picUrl, ex.getMessage()});
                   }
               }));
       executor.shutdown();
       try {
           executor.awaitTermination(15L, MILLISECONDS);
       } catch (InterruptedException ex) {
           log.log(Level.WARNING, "Could not wait for all images downloads");
       }
       Collections.sort(adPics); // null values at list lead to NPE here. How are there null values?
       return adPics;
   }