2016-08-11 6 views
4

リストにnull値があるため、NULLポインタ例外がありますadPics。まれにしか起こらない。どのように可能ですか?並べ替え中に非常に奇妙なNullPointerExceptionが発生しました

(並列にこのコードをダウンロード画像とローカルに保存されます。)

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

Stack trace## Heading ##

時々adPicsリストはnull値を有しています。それがNPEの理由です。しかしどうですか?スレッドで実行されたコードを分析すると、nullの値を追加することはできません。イメージのダウンロードに問題があった場合、IOExceptionがスローされます。 imageNewFileNamenullにはなりません。

このコードはJava 8で、Apache Commons IO libを使用しています。

+3

(約質問、downvotesではない)あなたは複数のスレッドからリストに追加しているが、リストが適切に同期されていないためだと思います。あなたがそれをすると面白いことが起こります。 – sstan

+5

あなたは 'awaitTermination'呼び出しがタイムアウトしていないことを確信していますし、同時に要素を追加しながら' adPics'のソートを開始しますか?なぜそれがNPEを投げるのか分かりませんが、 'ArrayList'はスレッドセーフではありませんので、何かが起こると思います。 – Tunaki

+0

うーん..ありがとう@sstan私はhttp://stackoverflow.com/questions/11360401/java-synchronized-list –

答えて

2

メソッドawaitTerminationは実行中のスレッドを停止しません。すべてのスレッドが完了するか、またはtimeoutに達するまで待ちます。したがって、あなたのスレッドは依然としてあなたのリストに項目を追加します。

また、タイムアウトに達してもファイルシステムへのダウンロードとコピーが実行中であることを考慮する必要があります。

シンプルだが完璧ではない解決策は、タイムアウトに達したときにフラグを設定し、アイテムを追加する前にフラグをチェックすることです。

より良いアプローチは、タイムアウトに達した後にスレッドを中断させることです。これには、ダウンロードとファイルのコピーの中断も含まれます。

+0

非常に興味深い。それを行う最もクリーンな方法は何でしょうか? –

+0

と、同時追加に関する問題についてコメントしている人はどうですか?両方の問題が混在していますか?彼らは独立していますか? –

+1

同期リストを使用している場合、何らかの形であなたの問題を偽装します。それはNPEを避けるべきです。しかし、バックグラウンドでは、ダウンロード、コピー、追加はまだ有効です。最終的には、ソート後に新しい項目が追加される可能性があるため、リストが確実にソートされることはありません。 –

0

コードにはいくつかの問題があります。パウロはすでに一つの問題を指摘している。他の問題は、List adPicsに同時にアクセスしていることです。

同期リストを使用せずに問題を解決するには、リスト[CompletableFuture]を作成し、終了時にはCompletableFuture.allOfを呼び出します。 各CompletableFutureはイメージファイル名を返すはずですが、イメージをダウンロードする前に、その操作を開始するかどうかを確認する必要があります。そうでない場合は、null値でCompletableFutureを完了できます。 CompletableFuture.allOfでは、null値なしで新しいListを作成し、そのリストをソートできます。

+0

同期リストを使用しても問題は解決されません。問題が発生すると、他の問題が発生します。たとえば、最終的なリストは確実にソートされません。 –

+0

私が同意したのは、同期されたリストを使わずにそれを解決する方法を考えた理由です。 – CodesInTheDark

関連する問題