2016-08-19 1 views
1

J'ai un programme dans lequel je télécharge des fichiers de S3 en utilisant un objet Callable soumis à un ExecutorService. Les fichiers sont volumineux et prennent plusieurs minutes pour être entièrement téléchargés. Est-il logique de créer une autre classe Callable qui prend l'avenir du téléchargeur et le regarde pour l'achèvement? Mon objectif final est d'ajouter tous les téléchargements complets à une liste centralisée dans un cache.Regarder à l'avenir <T> Objets à compléter avec ExecutorService

Par exemple:

public void add(final String s3urlToDownload){ 

    Future<S3Object> futureS3obj = cachedPoolExecutor.submit(new S3Downloader(s3urlToDownload)); 

    // Instead of calling futureS3obj.get() and waiting, submit the Future to the "Watcher" service. 
    // Within FutureWatcher, the S3Object will be added to the List once the download is complete. 
    cachedPoolExecutor.submit(new FutureWatcher(downloadedList, futureS3obj)) 

} 
+0

Quel est l'avantage d'un 'Callable' supplémentaire si vous avez déjà un' Future'? – Kayaman

+0

@Kayaman, À ma connaissance, si j'appelais la méthode 'future'' get() ', la méthode' add' bloquerait. C'est pourquoi je soumets un nouvel appelable 'FutureWatcher' – dmux

+0

Alors, qu'est-ce que' Callable' apporte? Appeler sa méthode 'call()' bloquerait aussi, puisqu'il délègue 'get()'. – Kayaman

Répondre

1

Voici quelques faux objets, utilisés à titre d'illustration. "Téléchargements" sont juste sommeils au hasard:

// for illustration only 
class S3Object { 
    String id; 
} 

// for illustration only 
class S3Downloader { 

    public S3Object download(String url) { 
     int min = 2; 
     int max = 5; 
     Random rand = new Random(); 
     int random = rand.nextInt((max - min) + 1) + min; 

     try { Thread.sleep(1000 * random); } catch (Exception ex) {} 
     S3Object result = new S3Object(); 
     result.id = url; 
     return result; 
    } 
} 

Nous pouvons définir une tâche qui télécharge le fichier, met à jour un (thread-safe) liste et Décrémentation CountDownLatch:

class MyTask implements Runnable { 
    private final List<S3Object> list; 
    private final CountDownLatch latch; 
    private final String url; 

    public MyTask(List<S3Object> list, CountDownLatch latch, String url) { 
     this.list = list; 
     this.latch = latch; 
     this.url = url; 
    }  

    public void run() { 
     S3Downloader downloader = new S3Downloader(); 
     S3Object result = downloader.download(url); 
     list.add(result); 
     latch.countDown(); 
    } 
} 

Un exemple Runner illustre le client". La méthode go est le pilote, et utilise la méthode add (qui ne bloque pas):

public class Runner { 
    private ExecutorService pool = Executors.newCachedThreadPool(); 
    private int numUrls = 20; 
    private CountDownLatch latch = new CountDownLatch(numUrls); 
    private List<S3Object> results = Collections.synchronizedList(new ArrayList<S3Object>()); 

    public void add(String url) { 
     pool.submit(new MyTask(results, latch, url)); 
    } 

    public void go() throws Exception { 

     for(int i = 0; i < numUrls; i++) { 
      String url = "http://example" + i; 
      add(url); 
     } 

     // wait for all downloads 
     latch.await(); 

     for (S3Object result : results) { 
      System.out.println("result id: " + result.id); 
     } 
    } 
} 

Code de production doit gérer les erreurs et éventuellement réorganiser le client, selon le cas.

+0

Merci Michael, je devrais être capable de refactoriser mon code pour inclure le cadre ci-dessus. – dmux

0

Au lieu de faire un « veilleur » qui consomme des ressources, faire le téléchargement en cours informer le maître à la fin.