开发者

Make threads usage efficient in Java

开发者 https://www.devze.com 2023-02-02 09:52 出处:网络
I have coded a simple application in Java that downloads particular images from a list of html links provided. Everything was working fine until I added the feature of having to download from a list o

I have coded a simple application in Java that downloads particular images from a list of html links provided. Everything was working fine until I added the feature of having to download from a list of html links rather than just one. I had to implement the wait() and notify() methods which forced me to change the approach a little. Now, the downloads work fine, but the GUI does not update while the download is in progress.

I make the 1st thread wait from HTML.java and notify it at the end of DownloadImages.java. For this I had to invoke buttonPressed class as an object rather than a thread, which is why I think 开发者_如何转开发my GUI won't update.

Is there a way to simplify or make thread-usage more efficient in my code?

Thanks in advance.

Here is skeleton of my code:

/*Test.java*/
package my;

import java.util.logging.Level;
import java.util.logging.Logger;

public class Test extends javax.swing.JFrame {

    public static buttonPressed bp;
    public static boolean alldone;

    /** Creates new form Test */
    public Test() {
        initComponents();
    }

    public static class buttonPressed implements Runnable {

        Thread t1, t2;

        buttonPressed() {
            t1 = new Thread(this, "downloadAction");
            t1.start();
        }

        public void suspendThread() {
            System.out.println("suspended");
            alldone = false;
        }

        public synchronized void resumeThread() {
            System.out.println("resumed");
            alldone = true;
            notify();
        }

        public void run() {
            String[] len = new String[]{/*list of urls*/};

            for (int i = 0; i < len.length; i++) {
                System.out.println("going times: " + i);

                t2 = new Thread(new HTML(), "HTMLthread");
                t2.start();

                synchronized (this) {
                    while (!alldone) {
                        try {
                            wait();
                        } catch (InterruptedException ex) {
                            Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
                        }
                    }
                }
            }
        }
    }

    private void downloadActionPerformed(java.awt.event.ActionEvent evt) {
        bp = new buttonPressed();
        try {
            bp.t1.join();
        } catch (InterruptedException e) {
            System.out.println("Main Thread: interrupted");
        }
    }

    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {

            public void run() {
                new Test().setVisible(true);
            }
        });
    }

    private javax.swing.JButton download;
    public static javax.swing.JProgressBar progress;
}

/*HTML.java*/
package my;

import java.util.ArrayList;

class HTML implements Runnable {
    private Thread t3;

    public HTML() {
        Test.bp.suspendThread();
    }

    public void run() {
        downloadHTML();
        ArrayList xyz = parseHTML();
        t3 = new Thread(new DownloadImages(xyz), "DownDecrypt");
        t3.start();
    }

    private void downloadHTML() {
        // Downloads the HTML file
    }

    private ArrayList parseHTML() {
        // Parses the HTML file and gets links to images
        return new ArrayList();
    }
}

/*DownloadImages.java*/
package my;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

class DownloadImages implements Runnable {

    static int current = 0, previous = 0;
    static boolean speedFlag;
    ArrayList<String> links = new ArrayList<String>();
    private Thread t4;

    public DownloadImages(ArrayList param1) {
        this.links = param1;
        speedFlag = true;
    }

    public void run() {
        t4 = new Thread(new getSpeed(), "getSpeed");
        t4.start();
        download(links);
    }

    private void download(ArrayList<String> param1) {
        String[] imgurl = new String[param1.size()];
        URLConnection conn = null;
        InputStream is = null;
        ByteArrayOutputStream bais = null;
        int prog;

        for (int i = 0; i < param1.size(); i++) {
            current = 0;
            imgurl[i] = param1.get(i);
            try {
                conn = new URL(imgurl[i]).openConnection();
                int fsize = conn.getContentLength();
                is = new BufferedInputStream(conn.getInputStream());
                bais = new ByteArrayOutputStream();

                byte[] byteChunk = new byte[1024];
                int n;
                while ((n = is.read(byteChunk)) > 0) {
                    bais.write(byteChunk, 0, n);
                    current = current + 1024;
                    prog = (int) (current * 100.0 / fsize);
                    Test.progress.setValue(prog);
                }
            } catch (MalformedURLException ex) {
                Logger.getLogger(DownloadImages.class.getName()).log(Level.SEVERE, null, ex);
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (is != null) {
                    try {
                        is.close();
                    } catch (IOException ex) {
                        Logger.getLogger(DownloadImages.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
            }

            byte[] imgBytes = bais.toByteArray();

            try {
                FileOutputStream fos = new FileOutputStream(i + ".jpg");
                fos.write(imgBytes);
                fos.flush();
                fos.close();
            } catch (FileNotFoundException ex) {
                System.out.println("FileNotFoundException : " + ex);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        speedFlag = false;
        // Resume the thread to start downloading the next link
        Test.bp.resumeThread();
    }

    private static class getSpeed implements Runnable {

        int kbytesPerSecond;
        private final int fireTime;

        public getSpeed() {
            fireTime = 1000;
        }

        public void run() {
            while (speedFlag) {
                try {
                    Thread.sleep(fireTime);
                } catch (InterruptedException ex) {
                    Logger.getLogger(getSpeed.class.getName()).log(Level.SEVERE, null, ex);
                }

                kbytesPerSecond = (((current - previous) / 1024) / (fireTime / 1000));
                System.out.println(kbytesPerSecond);

                previous = current;
            }
        }
    }
}


As far as the GUI is concerned you need to read about Swing concurrency. In short, use SwingWorker.

Mind that you use old AWT stuff (java.awt.EventQueue).


I suggest you have an ExecutorService like Executors.newCachedThreadPool and submit() the tasks to it. Collect the Future objects so you know when they are done. This will be more efficient and manageable than creating Threads all over the place.

You can have just one pool like

static final ExecutorService POOL = Executors.newCachedThreadPool();

to submit a task

POOL.submit(new Callable<Void>() {
    public Void call() throws InterruptedException {
        while (speedFlag) {
            Thread.sleep(1000);

            kbytesPerSecond = (current - previous) / 1024;
            System.out.println(kbytesPerSecond);

            previous = current;
        }
    }
});

Even better for repeating tasks is to use a scheduled executor service.

static final ScheduledExecutorService POOL  = Executors.newScheduledThreadPool(4);
Future task = POOL.scheduleAtFixedRate(new Runnable() {
    public void run()  {
        kbytesPerSecond = (current - previous) / 1024;
        System.out.println(kbytesPerSecond);
        previous = current;
    }
}, 1, 1, TimeUnit.SECONDS);
// to end the task
task.cancel(false);
0

精彩评论

暂无评论...
验证码 换一张
取 消