【问题标题】:ImageIO thread-safetyImageIO 线程安全
【发布时间】:2014-10-10 10:38:53
【问题描述】:

我在javax.imageio 规范中看到线程安全不是目标,尽管我已经看到了几个使用ImageIO.read()ImageIO.write() 在网络环境中上传/清理图像的示例。

所以,我的问题是,不管规范怎么说,ImageIO 线程安全吗?

【问题讨论】:

    标签: java javax.imageio


    【解决方案1】:

    至少在我的一个环境中,ImageIO 不是线程安全的(或者至少其中一个插件不是)。我正在调试从多个线程调用 ImageIO.read() 时无法正确加载 png 和 jpg 文件(有时是纯灰色,有时是反色,有时是随机颜色等)的问题。 我也偶尔会遇到 ConcurrentModificationExceptions,例如:

    java.util.ConcurrentModificationException
    at java.util.Vector$Itr.checkForComodification(Vector.java:1184)
    at java.util.Vector$Itr.next(Vector.java:1137)
    at sun.java2d.cmm.ProfileDeferralMgr.activateProfiles(ProfileDeferralMgr.java:93)
    at java.awt.color.ICC_Profile.getInstance(ICC_Profile.java:777)
    at com.sun.imageio.plugins.jpeg.JPEGImageReader.setImageData(JPEGImageReader.java:657)
    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 javax.imageio.ImageIO.read(ImageIO.java:1448)
    at javax.imageio.ImageIO.read(ImageIO.java:1308)
    at com.foo.bar.MyTestLoadThread.loadImage(MyTestLoadThread.java:241)
    ...
    at java.lang.Thread.run(Thread.java:745)
    

    我无法在所有环境中复制此行为,因此它可能是特定于 JVM 的。但这里是环境的细节。我看到它失败的地方:

    • 操作系统:Ubuntu 14.04
    • java 版本:
      java版本“1.8.0_33”
      Java(TM) SE 运行时环境 (build 1.8.0_33-b05)
      Java HotSpot(TM) 客户端虚拟机(内部版本 25.33-b05,混合模式)

    【讨论】:

    • ImageIO.write 也不是线程安全的。从多个线程调用它会导致工件:i.imgur.com/tiOZjQ7.png
    • 这个答案是指一个已知的 OpenJDK 错误,[JDK-6986863] (bugs.openjdk.java.net/browse/JDK-6986863),不幸的是仍然没有修复(在发布这篇文章时)。这是一个(线程?)问题,存在于 Java2D 颜色管理子系统中,但在 ImageIO 之外。
    • 它也发生在 Windows 上,所以至少在 Java 8 中这仍然是一个问题,应该解决。
    • 在带有 JDK 8 的 OSX 上发生
    • 这个 bug is finally fixed 现在在 JDK 17 中。
    【解决方案2】:

    TLDR;是的,静态方法ImageIO.read(...)write(...) 是线程安全的。


    说明"thread safety is not a goal" 的规范部分需要在上下文中阅读。规范实际上说的是,单个ImageReaderImageWriterImageInputStream/ImageOutputStream 实现不需要关心线程安全(因此,客户端代码永远不应该假设它们是线程安全的)。只要你遵守这条规则,你就是安全的。但是请注意,规范的同一部分还指出:

    [...] 同一个插件类的多个实例必须可以同时运行。

    这部分规范没有特别讨论ImageIO 的静态方法,但是上面的引用暗示这些方法是线程安全的,因为ImageIO.read(...)write(...) 创建了ImageInputStream/@987654331 的新实例@ 和 ImageReader/ImageWriter 用于每次调用。所以,这并不是真正的“尽管规范是这么说的”

    ImageIO 类由其他几个可以安全使用的静态方法组成,并且该类本身(大部分*)是无状态的。如果不是这样,那它就真的没有多大用处了……

    *) ImageIOIIORegistry 实例在类创建时填充,并在调用 scanForPlugins() 方法时重新初始化。如果两个线程同时调用它,您可能会遇到问题(即插件可能未正确注册),但由于客户端代码控制它发生的位置/时间,您可以轻松避免这种情况。还有每个线程组CacheInfo,但它的使用似乎是正确同步的。


    免责声明,我没有编写规范,但这是我的解释(我在无数多线程应用程序中使用了ImageIO,并且我自己编写了十几个ImageReaderImageWriter 插件) .

    【讨论】:

    • 据我了解,您是说使用具体的ImageReader 不安全,但使用ImageIO.read() 是安全的吗?
    • 使用 ImageReader 是完全安全的,甚至可以同时使用多个实例。它们只是不是线程安全的,也就是说,您不能在 多个线程 之间共享 单个实例(即 ImageReader 具有状态)。
    • 明白了,但是你可以在多个线程中同时使用一个ImageIO方法吗?
    • 是的,大多数方法(如readwrite 等)都可以(相信我,它们是)同时使用。
    • 我怀疑 ImageIO 不是线程安全的,因为当我将代码提升到 8 个线程时,我的 JVM 一直在崩溃。但后来我发现问题出在我的每线程代码中的其他内容上:我对每个图像的 OpenCL 处理不是线程安全的。当我在我的 OpenCL 东西周围放置一个同步块时,许多线程上的 ImageIO 都没有问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-11-21
    • 1970-01-01
    • 2021-07-12
    • 2011-03-30
    • 1970-01-01
    • 1970-01-01
    • 2012-09-03
    相关资源
    最近更新 更多