2016-08-11 3 views
4

목록에 null 값이 있으므로 null 포인터 예외가 있습니다. adPics. 거의 발생하지 않습니다. 그게 어떻게 가능해? 정렬 중에 매우 이상한 NullPointerException

(병렬로이 코드는 이미지를 다운로드하고 로컬로 저장합니다.)

Stack trace## Heading ##

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

때때로 adPics 목록 null 값이 있습니다. 그것이 NPE의 이유입니다. 그러나 어떻게? 스레드에서 실행 된 코드를 분석하면 null 값을 추가 할 수 없습니다. 이미지를 다운로드 할 때 문제가 발생하면 IOException이 발생합니다. imageNewFileNamenull 일 수 없습니다.

이 코드는 Java 8이며 Apache Commons IO lib를 사용합니다.

+3

(약 질문, downvotes) 나는 당신이 여러 스레드에서 목록에 추가하기 때문에 목록이 제대로 동기화되지 않은 것 같아요. 웃기는 일은 그렇게 할 때 일어날 수 있습니다. – sstan

+5

'awaitTermination' 호출이 시간 초과되지 않았으며,'adPics'를 정렬하면서 동시에 요소를 추가하고 있습니까? 왜 그것이 NPE를 던질 지 모르겠지만'ArrayList'는 스레드로부터 안전하지 않으므로 아무 일도 일어나지 않을 것입니다. – Tunaki

+0

흠 .. thanks @sstan http://stackoverflow.com/questions/11360401/java-synchronized-list –

답변

2

awaitTermination 메서드는 실행중인 스레드를 중지하지 않습니다. 모든 스레드가 완료되거나 timeout에 도달 할 때까지 기다립니다. 그러므로 스레드는 여전히 목록에 항목을 추가합니다.

또한 제한 시간에 도달해도 파일 시스템으로 다운로드 및 복사가 실행 중임을 고려해야합니다.

간단하지만 완벽한 해결책은 시간 초과에 도달하면 플래그를 설정하고 항목을 추가하기 전에 플래그를 확인하는 것입니다.

보다 나은 방법은 제한 시간에 도달 한 후에 스레드를 인터럽트하는 것입니다. 또한 다운로드 및 파일 복사를 중단해야합니다.

+0

매우 흥미 롭습니다. 그 일을하는 가장 깨끗한 방법은 무엇일까요? –

+0

그리고 동시 추가에 대해 사람들이 문제가되는 것에 관해서는 어떨까요? 두 가지 문제가 혼합되어 있습니까? 그들은 독립적입니까? –

+1

동기화 된 목록을 사용하는 경우 어떻게 든 문제를 가장합니다. NPE는 피해야합니다. 그러나 백그라운드에서 다운로드, 복사 및 추가는 여전히 활성화되어 있습니다. 결국 새 목록이 정렬 후에 추가 될 수 있으므로 목록이 더 이상 정렬되지 않습니다. –

0

코드에 몇 가지 문제가 있습니다. 바울은 이미 한 가지 문제를 지적했습니다. 다른 문제는 List adPics에 동시에 액세스한다는 것입니다.

동기화 된 목록을 사용하지 않고 문제를 해결하려면 List [CompletableFuture]를 만들고 CompletableFuture.allOf의 전화를 걸면됩니다. 각 CompletableFuture는 이미지 파일 이름을 반환해야하지만 이미지 다운로드를 시도하기 전에 시간을 확인하여 해당 작업을 시작할지 확인해야합니다. 그렇지 않으면 null 값으로 CompletableFuture를 완료 할 수 있습니다. CompletableFuture.allOf에서 null 값없이 새 List를 만들고 그 목록을 정렬 할 수 있습니다.

+0

동기화 된 목록을 사용하면 문제가 해결되지 않고 근본적인 문제 만 가장하여 다른 문제가 발생할 수 있습니다. 예를 들어 최종 목록은 확실히 정렬되지 않습니다. –

+0

나는 그것이 동기 목록을 사용하지 않고 그것을 해결하는 방법을 제안한 이유에 동의한다. – CodesInTheDark