2017-09-09 3 views
0

J'essaie d'écrire une classe ci-jointe pour télécharger des images du web qui seront actualisées toutes les 30 secondes. Je pourrais vouloir télécharger 1 image ou je pourrais vouloir télécharger des images N. Et je pourrais vouloir arrêter de télécharger une certaine image à tout moment. J'ai écrit la classe suivante qui fonctionne très bien sauf quand j'arrête de télécharger une mémoire d'image n'est pas publié pour cette tâche. Ou si j'arrête toutes les images d'être téléchargé la mémoire n'est pas libérée (Cela n'arrivera pas dans la production). J'ai essayé plusieurs façons d'y parvenir. Ma dernière tentative consistait à purger les tâches du ScheduledThreadPoolExecutor toutes les 30 secondes en utilisant le même exécuteur ou avec le code sous un exécuteur séparé. Je fournis également du code pour tester la libération de la mémoire, bien que mon exemple empêche toutes les images d'être téléchargées alors qu'en utilisation réelle, je ne devrais pouvoir arrêter qu'une image et libérer la mémoire de cette tâche.ScheduledThreadPoolExecutor fuite de mémoire de purge planifiée

import java.awt.image.BufferedImage; 
import java.io.IOException; 
import java.io.InputStream; 
import java.net.MalformedURLException; 
import java.net.URL; 
import java.net.URLConnection; 
import java.net.URLStreamHandler; 
import java.util.concurrent.ScheduledFuture; 
import java.util.concurrent.ScheduledThreadPoolExecutor; 
import java.util.concurrent.TimeUnit; 
import java.util.function.Consumer; 

import javax.imageio.IIOException; 
import javax.imageio.ImageIO; 
import javax.imageio.stream.ImageInputStream; 


public class ImageLoadTask implements Runnable { 

    private static ScheduledThreadPoolExecutor taskExecutorService = new ScheduledThreadPoolExecutor(500); 
    private static ScheduledThreadPoolExecutor purgeExecutorService = new ScheduledThreadPoolExecutor(500); 
    private static Runnable purgeRunnable =() -> purge(); 
    private ScheduledFuture<?> scheduledFuture; 
    private URL pictureURL; 
    private Consumer<BufferedImage> successMethod; 
    private Consumer<String> failMethod; 
    private ImageURLStreamHandler streamHandler = new ImageURLStreamHandler(); 

    private boolean displaySuccess = false; 
    private boolean displayError = false; 
    private boolean isCancelled = false; 

    static { 
     taskExecutorService.setExecuteExistingDelayedTasksAfterShutdownPolicy(false); 
     taskExecutorService.setContinueExistingPeriodicTasksAfterShutdownPolicy(false); 
     taskExecutorService.setRemoveOnCancelPolicy(true); 

     purgeExecutorService.scheduleWithFixedDelay(purgeRunnable, 30L, 30L, TimeUnit.SECONDS); 
    } 

    public ImageLoadTask(String url) { 
     try { 
      this.pictureURL = new URL(url); 
     } catch (MalformedURLException e) { 
      if(failMethod != null) { 
       failMethod.accept(e.getMessage()); ; 
      } 
      if(displayError) { 
       System.out.println("(ImageLoadTask) URL is malformed: " + url+"\n"+ e.getMessage()); 
      } 
     } 
    } 

    public ImageLoadTask(String url, Consumer<BufferedImage> successMethod) { 
     this(url); 
     this.successMethod = successMethod; 
    } 

    public ImageLoadTask(String url, Consumer<BufferedImage> successMethod, Consumer<String> failMethod) { 
     this(url, successMethod); 
     this.failMethod = failMethod; 
    } 

    public void start() { 
     scheduledFuture = taskExecutorService.scheduleAtFixedRate(this, 0L, 30L, TimeUnit.SECONDS); 
    } 

    public void stop() { 
     if(isCancelled) 
      return; 

     isCancelled = true; 
     scheduledFuture.cancel(true); 
     scheduledFuture = null; 
     pictureURL = null; 
     successMethod = null; 
     failMethod = null; 
     streamHandler = null; 

     taskExecutorService.remove(this); 
     taskExecutorService.purge(); 
    } 

    public static void purge() { 
     System.out.println("Purging"); 
     taskExecutorService.purge(); 
    } 

    @Override 
    public void run() { 
     if(!isCancelled) { 
      try { 
       BufferedImage image = loadImage(pictureURL); 
       if(displaySuccess) { 
        System.out.println("Image received for url " + pictureURL); 
       } 
       if(successMethod != null && !isCancelled) { 
        successMethod.accept(image); ; 
       } 
      } catch (IOException e) { 
       if(failMethod != null && !isCancelled) { 
        failMethod.accept(e.getMessage()); 
       } 
       if(displayError) { 
        System.out.println("Error occured retrieving image for url: " + pictureURL +"\n"+ e.getMessage()); 
       } 
      } 
     } 
    } 

    public void displayError(boolean displayError) { 
     this.displayError = displayError; 
    } 

    public void displaySuccess(boolean displaySuccess) { 
     this.displaySuccess = displaySuccess; 
    } 

    private BufferedImage loadImage(URL input) throws IOException { 
     if (input == null) { 
      throw new IllegalArgumentException("input == null!"); 
     } 

     InputStream istream = null; 
     try { 
      istream = streamHandler.openConnection(input).getInputStream(); 
     } catch (IOException e) { 
      throw new IIOException("Can't get input stream from URL!", e); 
     } 
     ImageInputStream stream = ImageIO.createImageInputStream(istream); 
     BufferedImage bi; 
     try { 
      bi = ImageIO.read(stream); 
      if (bi == null) { 
       stream.close(); 
      } 
     } finally { 
      istream.close(); 
     } 
     return bi; 
    } 

    @Override 
    protected void finalize() throws Throwable { 
     super.finalize(); 
     System.out.println("finalize"); 
    } 

    class ImageURLStreamHandler extends URLStreamHandler { 

     @Override 
     protected URLConnection openConnection(URL url) throws IOException { 
      URL target = new URL(url.toString()); 
      URLConnection connection = target.openConnection(); 
      // Connection settings 
      connection.setConnectTimeout(60000); // 1 min 
      connection.setReadTimeout(60000); // 1 min 
      return connection; 
     } 
    } 
} 

Test App:

import java.awt.EventQueue; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.awt.image.BufferedImage; 
import java.util.ArrayList; 
import java.util.List; 

import javax.swing.JButton; 
import javax.swing.JFrame; 
import javax.swing.JPanel; 

public class ImageLoadTaskTest { 

    public static void main(String[] args) { 
     EventQueue.invokeLater(new Runnable() { 
      @Override 
      public void run() { 
       Gui gui = new Gui(); 
      } 
     }); 
    } 

    static class Gui extends JFrame { 
     private static final long serialVersionUID = 1L; 

     private List<ImageLoadTask> tasks = new ArrayList<>(); 
     private boolean running = false; 

     private JButton startStopButton = new JButton("Start"); 
     private JButton purgeButton = new JButton("Purge"); 

     private ActionListener startStopListener = new ActionListener() { 
      @Override 
      public void actionPerformed(ActionEvent e) { 
       if(running) { 
        stopTasks(); 
       } else { 
        startTasks(); 
       } 
      } 
     }; 


     private ActionListener purgeListener = new ActionListener() { 
      @Override 
      public void actionPerformed(ActionEvent e) { 
       ImageLoadTask.purge(); 
      } 
     }; 

     public Gui() { 
      setTitle("Image Load Task Test"); 
      setBounds(250, 250, 300, 150); // Size 
      setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 

      JPanel contentPanel = new JPanel(); 
      setContentPane(contentPanel); 

      startStopButton.addActionListener(startStopListener); 
      contentPanel.add(startStopButton); 


      purgeButton.addActionListener(purgeListener); 
      contentPanel.add(purgeButton); 

      setVisible(true); 
     } 

     private void startTasks() { 
      running = true; 
      System.out.println("Starting tasks"); 
      for(int i = 0; i < 2500; i++) { 
       ImageLoadTask task = new ImageLoadTask("http://placehold.it/120x120&text=image" + i, this::success, this::fail); 
       task.start(); 
       tasks.add(task); 
      } 
      startStopButton.setText("Stop"); 
     } 

     private void stopTasks() { 
      running = false; 
      System.out.println("Stopping " + tasks.size() + " tasks"); 
      for(ImageLoadTask task : tasks) { 
       task.stop(); 
      } 
      tasks.clear(); 
      startStopButton.setText("Start"); 
      System.out.println("Stopped tasks "); 
      //ImageLoadTask.purge(); 
     } 

     private void success(BufferedImage image) { 
      //System.out.println("Success!"); 
     } 

     private void fail(String message) { 
      //System.out.println("Fail! "+ message); 
     } 
    } 
} 

Répondre

0

vous ne fermez pas votre 'flux' lorsque vous interrompez ImageIO.read(stream).

+0

Avez-vous une solution? J'ai essayé plusieurs choses et ça ne fonctionne toujours pas correctement. – rcantrel

+0

À la première étape, placez 'stream.close()' dans le bloc 'finally'. – Alexander

+0

Faites également 'isCancelled' AtomicBoolean car votre code n'est pas thread-safe. – Alexander