Java 发布多线程加载数千个图像,导致IOException
我在通过ForkJoinPool加载大量图像时遇到问题,我正在一个4核Intel上进行测试,该Intel具有超线程so 8逻辑线程。但是,我将池限制为仅4个线程。我收到来自ImageIO的错误,无法找到图像Java 发布多线程加载数千个图像,导致IOException,java,multithreading,javax.imageio,forkjoinpool,work-stealing,Java,Multithreading,Javax.imageio,Forkjoinpool,Work Stealing,我在通过ForkJoinPool加载大量图像时遇到问题,我正在一个4核Intel上进行测试,该Intel具有超线程so 8逻辑线程。但是,我将池限制为仅4个线程。我收到来自ImageIO的错误,无法找到图像 public class LoadImages extends RecursiveAction { private static final long serialVersionUID = 1L; //this is an example private static int thread
public class LoadImages extends RecursiveAction {
private static final long serialVersionUID = 1L;
//this is an example
private static int threadThreshold = totalImages/totalThreads + 2;
private String[] imgArr;
private int arrStart = 0;
private int arrSize = 0;
public LoadImages(String[] imgs, int start, int size) {
imgArr = imgs;
arrSize = size;
arrStart = start;
}
protected void processImages(){
BufferedImage img = null;
for (int i = arrStart; i < arrStart + arrSize; i++) {
try{
img = ImageIO.read(new File(imgArr[i]));
} catch (IOException | CMMException | NullPointerException e) {
System.out.println(imgArr[i]);
e.printStackTrace();
img = null;
}
...
}
}
protected void compute() {
// Check the number of files
if (arrSize <= threadThreshold) {
processImages();
return;
} else {
int split = arrSize / 2;
invokeAll(new LoadImages(imgArr, arrStart, split), new LoadImages(imgArr, arrStart + split, arrSize - split));
}
}
}
当我知道文件在那里的时候。
我将此代码用作指南:
似乎有点随机。我猜这可能只是硬件或操作系统错误。假设这是一个缩放问题,我对1700多幅图像的建议是,您最好在云上的某个地方设置它-如果您去检查源代码中的
ImageIO.read(文件)
和ImageIO.read(ImageInputStream),可以节省大量时间和麻烦
您可以看到,ImageIO
重用了ImageReader
的实例,并指出ImageReader
不是线程安全的。您可能需要创建自己的ImageReaders
,以便在单独的线程中使用
您还应该衡量这种多线程IO策略真正为您带来了多少好处。如果您试图从旋转的硬盘驱动器中提取Gig的图像数据,那么您的进程可能会受到I/O限制,并行加载不会给您带来太多好处 在我看来,当它在内部创建ImageInputStream时,似乎是一个ImageIO错误。您是否尝试使用ImageInputStream读取图像?比如:
InputStream is = new FileInputStream("path");
ImageInputStream iis = ImageIO.createImageInputStream(is);
BufferedImage bufImage = ImageIO.read(iis);
我同意这肯定是硬件/io硬盘的问题。然而,我试着在try/catch上做一个while循环,结果得到了一个无止境的循环。似乎一旦它无法访问文件,每次都会失败一次。。。但是如果我关闭程序,在一切正常的情况下再次运行它。。。真奇怪!但是如果我等一天再运行,我会遇到同样的问题,然后它第二次运行。但是,如果我将映像放置在NAS上,如Thecus(当前备份存储)。我可以让它每次都失败,不管我运行程序多少次,它实际上节省了很多时间。因为SSD似乎从未出现故障(只有旋转的磁盘驱动器,这让我觉得可能是硬件问题)。用4个线程加载大约5gb的图像确实需要1/3-1/4的时间。我也觉得这不是ImageIO造成的问题,因为如果我使用InputStream,它仍然失败,我将尝试在每个线程中发出一个新的读取器,看看这是否解决了问题。谢谢这里没有骰子,似乎是一个相关的问题。然而,在阅读本文时:结论似乎是Java对ImageIO的主要关注点不是线程安全,但它似乎仍然是。ImageReader的线程安全性不高并不意味着不能在多线程环境中使用它们。不幸的是,这是一个常见的误解。。这意味着不能在多个线程之间共享
ImageReader
的单个实例。从多个线程使用ImageIO.read()
是安全的。刚刚尝试过,这似乎仍然是一个相关的问题:java.io.FileNotFoundException:C:\jpg\IMG_7734.jpg(系统无法打开该文件)在java.io.FileInputStream.open(本机方法)在java.io.FileInputStream。(未知源代码)在java.io.FileInputStream。(未知源代码)文件肯定在那里。你是否尝试同步读取?它总是指向相同的文件还是随机的?我不确定我是否理解你的意思,数组是在不同于walkFileTree的类中构建的。我应该把它放在什么地方?InputStream?您的第一个错误是在compute()和invokeall()中。第一个LoadImages应该使用fork(),第二个LoadImages应该使用compute()。查看用于ForkJoinPool的JavaDoc。invokeAll()等待所有调用的任务完成,然后再继续。@edharned我明白你的意思了,我试过了,实际上是从那里开始的。但是如果我加载img1.fork();loadImg2.compute();loadImg1.join();它也做同样的事我错了吗?看看我下面的例子:无论哪种方式,我仍然会得到相同的错误。为了更快,请尝试设置ImageIO.useCache(false)
。此外,我认为您应该使用下面答案之一的注释中提到的IOException
更新问题。这很可能就是原因。也许您的进程可以有多少打开的文件或类似的文件达到了限制?我在标题中添加了IOException,并尝试将useCache设置为false,看看是否有帮助。我仍然认为使用带有线程池的队列会更容易(生产者/消费者)。使用FJ进行异步处理有限制。你可以直接计算你需要的任务数量,因为你不需要等待完成。您所得到的错误可能是@Wes Cumberland在下面所说的。似乎您将并发性与并行性混为一谈。
InputStream is = new FileInputStream("path");
ImageInputStream iis = ImageIO.createImageInputStream(is);
BufferedImage bufImage = ImageIO.read(iis);