排序时非常奇怪的NullPointerException

时间:2016-08-11 18:02:51

标签: java multithreading executorservice

我有一个空指针异常,因为列表adPics上有一些空值。它很少发生。怎么可能?

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

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;
   }

http://jsfiddle.net/q1kumbea/

有时adPics列表中包含null个值。这就是NPE的原因。但是怎么样?分析线程中执行的代码,无法添加null值。如果下载图像时出现问题,则会抛出IOException。 imageNewFileName无法null

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

2 个答案:

答案 0 :(得分:2)

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

此外,即使达到超时,您也应该考虑下载和复制到文件系统。

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

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

答案 1 :(得分:0)

您的代码存在一些问题。保罗已经指出了一个问题。其他问题是您同时访问List adPics。

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