【问题标题】:Java - Multithreading with ImageIOJava - ImageIO 多线程
【发布时间】:2015-12-15 03:19:58
【问题描述】:

我有一个加载缓慢的程序,我猜这是由于我必须在开始时加载的图像资源量。我认为多线程会有所帮助,但现在我不太确定。这是我的自动多线程方法。

    private static Thread[] t;

    private static int currentThreads;

    public static void loadWithThreads(Object[] array, IntegerRunnable r) {

        final int threads =  Runtime.getRuntime().availableProcessors();
        t = new Thread[threads];

        for (int i = 0; i < threads; i ++) {
            t[i] = new Thread("HMediaConverter") {

                final int id = currentThreads;

                int items = (array.length / threads) * currentThreads;


                @Override
                public void run() {

                    super.run();

                    for (int i = items; i < (items + (array.length / threads)); i ++) {
                        r.run(i);
                    }

                    //Recycle this thread so it can be used for another time.
                    try {
                        t[id].join();
                        lock.notifyAll();
                        currentThreads --;
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                }


            };
            t[i].setPriority(Thread.MAX_PRIORITY);
            t[i].start();
            currentThreads ++;
        }
    }

这是我的图片加载代码:

public static ImageIcon loadImageIcon(String path) {
    return new ImageIcon(ImageIO.read(Tools.class.getClassLoader().getResource(path));
}

肯定有办法加快速度吗?我在非常好的 Intel i5 上运行它,它不应该这么慢,所以它一定是我的代码。

【问题讨论】:

  • 改用ExecutorService,可能无法解决问题,但它会比你现在做的更干净
  • 另外,一旦Threadrun方法存在,就无法重新启动
  • @MadProgrammer 请问您说run() 方法无法重新启动时指的是什么?我不太明白你在说什么。感谢您的快速反馈;我很感激。
  • t[id].join(); 将死锁,因为当前线程在join 返回之前无法退出,但joinrun 方法退出之前不会返回
  • “请问你说run()方法不能重启是指什么?” - 线程是不可重入的,也就是说,一旦run 方法存在,你就不能重复使用Thread 的实例,所以//Recycle this thread so it can be used for another time. 不起作用,Thread 将无法重新启动。如果不了解您要加载的内容、资源数量和大小,也无法评论是否会起作用(或一个好主意)

标签: java multithreading javax.imageio


【解决方案1】:

正在加载总共 159.14mb 的 113 张图片...

public static void loadWithoutThreads(File[] array) {
    for (File file : array) {
        try {
            ImageIO.read(file);
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
}

耗时约 15 秒

随着...

public static void loadWithThreads(File[] array) {

    final int threads = Runtime.getRuntime().availableProcessors();
    t = new Thread[threads];

    CountDownLatch latch = new CountDownLatch(threads);

    for (int i = 0; i < threads; i++) {
        t[i] = new Thread("HMediaConverter") {
            final int id = currentThreads;

            int items = (array.length / threads) * currentThreads;

            @Override
            public void run() {
                try {
                    System.out.println("Starting " + id);

                    for (int i = items; i < (items + (array.length / threads)); i++) {
                        try {
                            System.out.println(i + ": " + array[i]);
                            ImageIO.read(array[i]);
                        } catch (IOException ex) {
                            ex.printStackTrace();
                        }
                    }
                } finally {
                    latch.countDown();
                }

            }

        };
        t[i].setPriority(Thread.MAX_PRIORITY);
        System.out.println("Start " + i);
        t[i].start();
        currentThreads++;
    }

    try {
        latch.await();
    } catch (InterruptedException ex) {
        ex.printStackTrace();
    }
}

花了大约 11 秒

随着……

public static void loadWithExecutor(File[] images) {
    ExecutorService service = Executors.newFixedThreadPool(2);
    List<ImageLoadingTask> tasks = new ArrayList<>(images.length);
    for (File file : images) {
        tasks.add(new ImageLoadingTask(file));
    }
    try {
        List<Future<BufferedImage>> results = service.invokeAll(tasks);
    } catch (InterruptedException ex) {
        ex.printStackTrace();
    }
    service.shutdown();
}

public static class ImageLoadingTask implements Callable<BufferedImage> {

    private File file;

    public ImageLoadingTask(File file) {
        this.file = file;
    }

    @Override
    public BufferedImage call() throws Exception {
        return ImageIO.read(file);
    }

}

耗时约 7 秒

ExecutorService 效率更高,因为当一个线程处理较大的文件时,另一个线程可以处理多个小文件。这是通过池化那些在需要之前不做任何工作的线程来实现的,允许一个线程执行很多短的工作,而其他线程也很忙。不用等那么久

查看Executors了解更多详情

【讨论】:

    【解决方案2】:

    以下是一个重写,应该与操作所写的内容接近。重新写入固定大小的线程池可能会更好。

    //import java.util.concurrent.atomic.AtomicInteger;
    
    private static Thread[] t;
    
        private static AtomicInteger completedLoads = new AtomicInteger(0);
    
        public static void loadWithThreads(Object[] array, IntegerRunnable r) {
    
            final int threads =  Runtime.getRuntime().availableProcessors();
            t = new Thread[threads];
            completedLoads = new AtomicInteger(0);
            int targetLoads = array.length;
            int itemsPerThread = (array.length / threads);
    
            for (int i = 0; i < threads; i ++) {
                t[i] = new Thread("HMediaConverter" + i) {
    
                    int startItem = itemsPerThread * i;
    
                    @Override
                    public void run() {
    
                        super.run();
    
                        for (int i = startItem; i < startItem + itemsPerThread; i ++) {
                            try {
                                r.run(i);
                             }
                             finally {
                                     completedLoads.incrementAndGet();
                             }
                        }
                    }
                };
                t[i].setPriority(Thread.MAX_PRIORITY);
                t[i].start();
            }
    
            // Wait for the images to load    
            while (completedLoads.get() < targetLoads)
            {
                    try {
                            Thread.sleep(100);
                    }
                    catch (InterruptedException ie) {
                            // ignore
                    }
            }
        }
    

    【讨论】:

    • 快速研究一下CountDownLatch,你的while (completedLoads.get() &lt; targetLoads) 是多余和浪费的
    【解决方案3】:

    找出减速的​​部分——例如通过运行 System.currentTimeMillis() btween major segmnst 然后向我们展示最大时间在哪里——或者向我们展示所有程序。

    如上所述的线程处理是有问题的,你不应该使用开箱即用的方法,例如 join 等,除非你在某个地方看到它可以证明工作。

    所以发布时间,我们将从那里获取 - 它可能是图像,也可能是线程

    【讨论】:

    • 听起来不错;但是我会说,添加此线程将速度提高了几百毫秒,但并没有我想要的那么多。
    猜你喜欢
    • 2012-02-15
    • 1970-01-01
    • 1970-01-01
    • 2016-01-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多