首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >ImageIO -读取不同的文件并发问题?

ImageIO -读取不同的文件并发问题?
EN

Stack Overflow用户
提问于 2018-05-22 06:38:22
回答 2查看 952关注 0票数 0

我目前正在编写一个程序,它可以批量处理不同的图像。因此,我认为并行执行这个操作(缩放/添加水印)可能是明智的。

问题是,我得到了以下错误:

代码语言:javascript
复制
    Exception in thread "main" java.util.concurrent.ExecutionException: java.lang.NullPointerException
    at java.base/java.util.concurrent.ForkJoinTask.get(ForkJoinTask.java:1006)
    at eu.reisihub.soft.watermarking.Main$main$2$2$2$2.invoke(Main.kt:62)
    at eu.reisihub.soft.watermarking.Main$main$2$2$2$2.invoke(Main.kt:19)
    at kotlin.sequences.TransformingSequence$iterator$1.next(Sequences.kt:149)
    at kotlin.sequences.TransformingSequence$iterator$1.next(Sequences.kt:149)
    at kotlin.sequences.SequencesKt___SequencesKt.count(_Sequences.kt:1006)
    at eu.reisihub.soft.watermarking.Main.main(Main.kt:66)
Caused by: java.lang.NullPointerException
    at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:488)
    at java.base/java.util.concurrent.ForkJoinTask.getThrowableException(ForkJoinTask.java:603)
    ... 7 more
Caused by: java.lang.NullPointerException
    at java.desktop/java.awt.color.ICC_Profile.intFromBigEndian(ICC_Profile.java:1784)
    at java.desktop/java.awt.color.ICC_Profile.getNumComponents(ICC_Profile.java:1476)
    at java.desktop/sun.java2d.cmm.lcms.LCMSTransform.<init>(LCMSTransform.java:93)
    at java.desktop/sun.java2d.cmm.lcms.LCMS.createTransform(LCMS.java:173)
    at java.desktop/java.awt.color.ICC_ColorSpace.fromRGB(ICC_ColorSpace.java:230)
    at java.desktop/com.sun.imageio.plugins.jpeg.JPEGImageReader.setImageData(JPEGImageReader.java:808)
    at java.desktop/com.sun.imageio.plugins.jpeg.JPEGImageReader.readImageHeader(Native Method)
    at java.desktop/com.sun.imageio.plugins.jpeg.JPEGImageReader.readNativeHeader(JPEGImageReader.java:723)
    at java.desktop/com.sun.imageio.plugins.jpeg.JPEGImageReader.checkTablesOnly(JPEGImageReader.java:347)
    at java.desktop/com.sun.imageio.plugins.jpeg.JPEGImageReader.gotoImage(JPEGImageReader.java:493)
    at java.desktop/com.sun.imageio.plugins.jpeg.JPEGImageReader.readHeader(JPEGImageReader.java:716)
    at java.desktop/com.sun.imageio.plugins.jpeg.JPEGImageReader.readInternal(JPEGImageReader.java:1173)
    at java.desktop/com.sun.imageio.plugins.jpeg.JPEGImageReader.read(JPEGImageReader.java:1153)
    at java.desktop/javax.imageio.ImageIO.read(ImageIO.java:1468)
    at java.desktop/javax.imageio.ImageIO.read(ImageIO.java:1363)
    at eu.reisihub.shot.UtilsKt.readImage(Utils.kt:19)
    at eu.reisihub.soft.watermarking.WatermarkUtils$create$1.invoke(WatermarkUtils.kt:18)
    at eu.reisihub.soft.watermarking.WatermarkUtils$create$1.invoke(WatermarkUtils.kt:8)
    at eu.reisihub.soft.watermarking.Main$sam$java_util_concurrent_Callable$0.call(Main.kt)
    at java.base/java.util.concurrent.ForkJoinTask$AdaptedCallable.exec(ForkJoinTask.java:1448)
    at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
    at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1603)
    at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:177)

请注意,我知道java.desktop/java.awt.color.ICC_Profile.intFromBigEndian(ICC_Profile.java:1784)中有NPE。相信我,这不是形象。

我用Java 8和Java 10尝试了2种方法,我有一个固定大小的线程池,有6个线程,用于4个内核。我使用Kotlin作为实现,并创建了一个扩展函数fun Path.readImage(): BufferedImage。对于不认识Kotlin的人来说,仅就这个例子而言,大致相当于public BufferedImage readImage(Path this)

朴素方法使用Files.newInputStream(this, StandardOpenOption.READ).use { ImageIO.read(it) }读取文件。这将从路径打开一个新的InputStream,并告诉ImageIO从这个InputStream读取图像。看起来还好吗?每第四次在Java 8上运行一次,在Java 10上大约有90%不起作用。顺便说一句:如果我把一个同步块围绕在一起,它就会工作--总是这样。用13秒来处理17张图像,而不是7秒。

同时,我将Gradle从4.4更新到4.7。在我将IntelliJ配置为使用Java 10和Gradle使用Java 8之前,可能是上述差异的原因。旁注。我一直用synchronized(System.err)

复杂的方法知道它在同步所有东西时起作用,我的方法现在看起来更复杂,如下所示:

代码语言:javascript
复制
fun Path.readImage(): BufferedImage = Files.newInputStream(this, StandardOpenOption.READ).buffered().use {
    var nStream: ImageInputStream? = null
    var nReader: ImageReader? = null
    synchronized(System.err) {
        nStream = ImageIO.createImageInputStream(it) ?: throw IIOException("Can't create an ImageInputStream!")

        val iter = ImageIO.getImageReaders(nStream)
        if (!iter.hasNext()) {
            throw IIOException("No image nReader found!")
        }
        nReader = iter.next()
    }

    nStream!!.let { stream ->
        nReader!!.let { reader ->
            reader.setInput(stream, true, true)
            try {
                println(reader)
                return reader.read(0, null)
            } finally {
                reader.dispose()
                stream.close()
            }
        }
    }
}

看到代码中的println了吗?我知道,对于每一个图像,一个新的com.sun.imageio.plugins.jpeg.JPEGImageReader对象是created.This,它将性能提高到我交谈的7秒。通常不工作2-4次,然后工作6-10次。

复杂版本的堆栈跟踪如下:

代码语言:javascript
复制
Exception in thread "main" java.util.concurrent.ExecutionException: java.lang.NullPointerException
    at java.util.concurrent.FutureTask.report(FutureTask.java:122)
    at java.util.concurrent.FutureTask.get(FutureTask.java:192)
    at eu.reisihub.soft.watermarking.Main$main$1$2$2$2$2.invoke(Main.kt:64)
    at eu.reisihub.soft.watermarking.Main$main$1$2$2$2$2.invoke(Main.kt:20)
    at kotlin.sequences.TransformingSequence$iterator$1.next(Sequences.kt:149)
    at kotlin.sequences.TransformingSequence$iterator$1.next(Sequences.kt:149)
    at kotlin.sequences.SequencesKt___SequencesKt.count(_Sequences.kt:1006)
    at eu.reisihub.soft.watermarking.Main$main$1.invoke(Main.kt:68)
    at eu.reisihub.soft.watermarking.Main$main$1.invoke(Main.kt:20)
    at eu.reisihub.shot.UtilsKt.measured(Utils.kt:54)
    at eu.reisihub.soft.watermarking.Main.main(Main.kt:24)
Caused by: java.lang.NullPointerException
    at java.awt.color.ICC_Profile.intFromBigEndian(ICC_Profile.java:1782)
    at java.awt.color.ICC_Profile.getNumComponents(ICC_Profile.java:1474)
    at sun.java2d.cmm.lcms.LCMSTransform.<init>(LCMSTransform.java:98)
    at sun.java2d.cmm.lcms.LCMS.createTransform(LCMS.java:173)
    at java.awt.color.ICC_ColorSpace.fromRGB(ICC_ColorSpace.java:218)
    at com.sun.imageio.plugins.jpeg.JPEGImageReader.setImageData(JPEGImageReader.java:694)
    at com.sun.imageio.plugins.jpeg.JPEGImageReader.readImageHeader(Native Method)
    at com.sun.imageio.plugins.jpeg.JPEGImageReader.readNativeHeader(JPEGImageReader.java:609)
    at com.sun.imageio.plugins.jpeg.JPEGImageReader.checkTablesOnly(JPEGImageReader.java:347)
    at com.sun.imageio.plugins.jpeg.JPEGImageReader.gotoImage(JPEGImageReader.java:481)
    at com.sun.imageio.plugins.jpeg.JPEGImageReader.readHeader(JPEGImageReader.java:602)
    at com.sun.imageio.plugins.jpeg.JPEGImageReader.readInternal(JPEGImageReader.java:1059)
    at com.sun.imageio.plugins.jpeg.JPEGImageReader.read(JPEGImageReader.java:1039)
    at eu.reisihub.shot.UtilsKt.readImage(Utils.kt:39)
    at eu.reisihub.soft.watermarking.WatermarkUtils$create$1.invoke(WatermarkUtils.kt:16)
    at eu.reisihub.soft.watermarking.WatermarkUtils$create$1.invoke(WatermarkUtils.kt:8)
    at eu.reisihub.soft.watermarking.Main$sam$java_util_concurrent_Callable$0.call(Main.kt)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:748)

其中Main:64Future#getMain:68kotlin.Sequence#count

定位Main.kt文件:https://github.com/reisi007/Reisishot-Photo-Tools/blob/ea32a605657226edb6140911fd02f77d006163d3/Watermarking/src/main/kotlin/eu/reisihub/soft/watermarking/Main.kt

Utils.kt文件的位置:https://github.com/reisi007/Reisishot-Photo-Tools/blob/ea32a605657226edb6140911fd02f77d006163d3/base/src/main/kotlin/eu/reisihub/shot/Utils.kt

我已经尝试了几个小时的代码改进,我的头脑现在停止产生新的想法。我知道这个错误也发生在两张照片上。由于它并不总是失败,国际商会的概况必须是正确的。我-老实说-认为我的硬盘不健全,偶尔也会搞砸。我还试着从我的SSD中读取文件。根据https://stackoverflow.com/a/26300361/1870799,读取方法应该是线程安全的。我不知道发生了什么。磁盘上的每个图像(由路径表示)都会获得自己的Callable<Task>。我只阅读这些图片,并将其写入另一个文件夹。所以前面的任务一个文件被读取,一个文件被写入。这两个文件不一样,每个任务都有自己的文件。

他们唯一共享的是一个PNG格式的BufferedImage,它是在读取的图像(水印)上绘制的。但这会在以后发生。在堆栈跟踪中可以看到,在读取JPEG图像时会发生这种情况。

代码语言:javascript
复制
at com.sun.imageio.plugins.jpeg.JPEGImageReader.read(JPEGImageReader.java:1039)
at eu.reisihub.shot.UtilsKt.readImage(Utils.kt:39)

如果要执行该程序,可以使用https://github.com/reisi007/Reisishot-Photo-Tools/blob/ea32a605657226edb6140911fd02f77d006163d3/Watermarking/src/main/kotlin/eu/reisihub/soft/watermarking/SettingsCreator.kt创建JSON设置,其主应用程序所需的路径

我很感谢你的意见。现在我不知道是怎么回事.

顺便说一句:对于我正在使用的一些任务:https://github.com/coobird/thumbnailator

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2018-05-22 13:35:25

2018-05-23

@haraldK在一条评论中提到,在代码之前加载图像配置文件:

代码语言:javascript
复制
import java.awt.color.ColorSpace
import java.awt.color.ICC_Profile
import java.awt.image.BufferedImage
import java.nio.file.Path
import javax.imageio.ImageIO

object ImgLoadUtils {
    init {
        // Load deferred color space profiles to avoid 
        // ConcurrentModificationException due to JDK
        // Use in public static main void or prior to application initialization
        // https://github.com/haraldk/TwelveMonkeys/issues/402
        // https://bugs.openjdk.java.net/browse/JDK-6986863
        // https://stackoverflow.com/questions/26297491/imageio-thread-safety
        ICC_Profile.getInstance(ColorSpace.CS_sRGB).getData();
        ICC_Profile.getInstance(ColorSpace.CS_PYCC).getData();
        ICC_Profile.getInstance(ColorSpace.CS_GRAY).getData();
        ICC_Profile.getInstance(ColorSpace.CS_CIEXYZ).getData();
        ICC_Profile.getInstance(ColorSpace.CS_LINEAR_RGB).getData();
    }

    fun loadImage(p: Path): BufferedImage = ImageIO.read(p.toFile())
}

来源

感谢@Alex的OpenJDK bug链接。这让我确信我不是个十足的白痴。

而不是

代码语言:javascript
复制
fun Path.readImage(): BufferedImage =
    Files.newInputStream(this, StandardOpenOption.READ).use { ImageIO.read(it) }

我现在用

代码语言:javascript
复制
fun Path.readImage(): BufferedImage =
    ImageIcon(toUri().toURL()).let {
        BufferedImage(it.iconWidth, it.iconHeight, BufferedImage.TYPE_INT_ARGB).apply {
            it.paintIcon(null, createGraphics(), 0, 0)
        }
    }

这是per https://aacsinia.wordpress.com/2010/12/21/java-how-to-create-buffered-image-from-inputstream/。速度正常,错误消失了,,20+,运行!因此,不应在多线程环境中使用IMHO ImageIO.read .

票数 3
EN

Stack Overflow用户

发布于 2018-05-22 07:23:49

在多线程应用程序中,有一个错误报告用于OpenJDK,名为'NullPointerException from ICC_Profile.getInstance()‘。在撰写本报告时,没有解决办法。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/50461289

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档