【发布时间】:2014-10-10 10:38:53
【问题描述】:
我在javax.imageio 规范中看到线程安全不是目标,尽管我已经看到了几个使用ImageIO.read() 和ImageIO.write() 在网络环境中上传/清理图像的示例。
所以,我的问题是,不管规范怎么说,ImageIO 线程安全吗?
【问题讨论】:
标签: java javax.imageio
我在javax.imageio 规范中看到线程安全不是目标,尽管我已经看到了几个使用ImageIO.read() 和ImageIO.write() 在网络环境中上传/清理图像的示例。
所以,我的问题是,不管规范怎么说,ImageIO 线程安全吗?
【问题讨论】:
标签: java javax.imageio
至少在我的一个环境中,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 的。但这里是环境的细节。我看到它失败的地方:
【讨论】:
ImageIO 之外。
Windows 上,所以至少在 Java 8 中这仍然是一个问题,应该解决。
TLDR;是的,静态方法ImageIO.read(...) 和write(...) 是线程安全的。
说明"thread safety is not a goal" 的规范部分需要在上下文中阅读。规范实际上说的是,单个ImageReader、ImageWriter 或ImageInputStream/ImageOutputStream 实现不需要关心线程安全(因此,客户端代码永远不应该假设它们是线程安全的)。只要你遵守这条规则,你就是安全的。但是请注意,规范的同一部分还指出:
[...] 同一个插件类的多个实例必须可以同时运行。
这部分规范没有特别讨论ImageIO 的静态方法,但是上面的引用暗示这些方法是线程安全的,因为ImageIO.read(...) 和write(...) 创建了ImageInputStream/@987654331 的新实例@ 和 ImageReader/ImageWriter 用于每次调用。所以,这并不是真正的“尽管规范是这么说的”。
ImageIO 类由其他几个可以安全使用的静态方法组成,并且该类本身(大部分*)是无状态的。如果不是这样,那它就真的没有多大用处了……
*) ImageIO 的 IIORegistry 实例在类创建时填充,并在调用 scanForPlugins() 方法时重新初始化。如果两个线程同时调用它,您可能会遇到问题(即插件可能未正确注册),但由于客户端代码控制它发生的位置/时间,您可以轻松避免这种情况。还有每个线程组CacheInfo,但它的使用似乎是正确同步的。
免责声明,我没有编写规范,但这是我的解释(我在无数多线程应用程序中使用了ImageIO,并且我自己编写了十几个ImageReader 和ImageWriter 插件) .
【讨论】:
ImageReader 不安全,但使用ImageIO.read() 是安全的吗?
ImageIO方法吗?
read、write 等)都可以(相信我,它们是)同时使用。