最初我還以爲主要原因可能是concurrent write
操作。由於寫入量小於2 MB,磁盤I/O通常應該沒有瓶頸。經過一番調查後,我找到了原因。在你的情況下,ImageIO
正在使用一種同步的方法(sun.java2d.cmm.lcms.LCMSTransform.doTransform
)。
我用這個小代碼來確認你發現的行爲並找到鎖定條件。
package sub.optimal.jai;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.Transparency;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.ComponentColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.MemoryImageSource;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.imageio.ImageIO;
public class ThreadedOutput implements Callable<Boolean> {
private final String fileName;
private ThreadedOutput(String name) {
this.fileName = name;
}
@Override
public Boolean call() throws Exception {
Thread.currentThread().setName("convert: " + fileName);
return this.storeImage();
}
public boolean storeImage() throws IOException {
byte[] imgArray = new byte[512 * 512];
int canvasSize = 512;
int value = 0;
for (int i = 0; i < imgArray.length; i++) {
imgArray[i] = (byte) value;
value = (++value & 0xFF);
}
ColorModel colorModel = new ComponentColorModel(
ColorSpace.getInstance(ColorSpace.CS_GRAY), null, false,
false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
Image image = Toolkit.getDefaultToolkit().createImage(
new MemoryImageSource(canvasSize, canvasSize, colorModel,
imgArray, 0, canvasSize)
);
BufferedImage bimage = new BufferedImage(canvasSize, canvasSize,
BufferedImage.TYPE_BYTE_GRAY);
Graphics2D bGr = bimage.createGraphics();
bGr.setPaintMode();
System.out.printf("start %s%n", fileName);
long start = System.currentTimeMillis();
bGr.drawImage(image, 0, 0, null);
long end = System.currentTimeMillis();
System.out.printf("duration drawimage: %s %d%n", fileName, end-start);
bGr.dispose();
return ImageIO.write(bimage, "PNG", new File("/tmp/" + fileName));
}
public static void main(String[] args) throws Exception {
System.out.println("CPUs: " + Runtime.getRuntime()
.availableProcessors());
ExecutorService executor = Executors.newFixedThreadPool(8);
List<ThreadedOutput> callables = new ArrayList<>();
callables.add(new ThreadedOutput("file1.png"));
callables.add(new ThreadedOutput("file2.png"));
callables.add(new ThreadedOutput("file3.png"));
callables.add(new ThreadedOutput("file4.png"));
callables.add(new ThreadedOutput("file5.png"));
callables.add(new ThreadedOutput("file6.png"));
callables.add(new ThreadedOutput("file7.png"));
callables.add(new ThreadedOutput("file8.png"));
System.out.println("execute creation in sequence");
long start = System.currentTimeMillis();
for (ThreadedOutput callable : callables) {
callable.call();
}
long end = System.currentTimeMillis();
System.out.printf("duration in sequence: %d%n", end - start);
System.out.println("execute creation in parallel");
start = System.currentTimeMillis();
executor.invokeAll(callables);
executor.shutdown();
end = System.currentTimeMillis();
System.out.printf("duration in threads: %d%n", end - start);
}
}
執行以下示例輸出
CPUs: 4
execute creation in sequence
start file1.png
duration drawimage: file1.png 1021
start file2.png
duration drawimage: file2.png 1021
start file3.png
duration drawimage: file3.png 1230
start file4.png
duration drawimage: file4.png 1056
start file5.png
duration drawimage: file5.png 1046
start file6.png
duration drawimage: file6.png 835
start file7.png
duration drawimage: file7.png 983
start file8.png
duration drawimage: file8.png 952
duration in sequence: 8549
execute creation in parallel
start file1.png
start file4.png
start file2.png
start file3.png
start file6.png
start file8.png
start file5.png
start file7.png
duration drawimage: file6.png 18889
duration drawimage: file1.png 19147
duration drawimage: file8.png 19204
duration drawimage: file5.png 19353
duration drawimage: file7.png 19435
duration drawimage: file3.png 19498
duration drawimage: file2.png 19582
duration drawimage: file4.png 19591
duration in threads: 19612
在八個並行線程運行創建所產生的代碼是顯着更慢。
在創建過程中的一個線程轉儲(與jstack $pid_of_example
)行execute creation in parallel
打印,你會發現類似的線
"convert: file1.png" #13 prio=5 os_prio=0 tid=...
java.lang.Thread.State: RUNNABLE
at sun.java2d.cmm.lcms.LCMS.colorConvert(Native Method)
at sun.java2d.cmm.lcms.LCMSTransform.doTransform(LCMSTransform.java:161)
- locked <0x00000000c463a080> (a sun.java2d.cmm.lcms.LCMSTransform)
"convert: file2.png" #14 prio=5 os_prio=0 tid=...
java.lang.Thread.State: BLOCKED (on object monitor)
at sun.java2d.cmm.lcms.LCMSTransform.doTransform(LCMSTransform.java:140)
- waiting to lock <0x00000000c463a080> (a sun.java2d.cmm.lcms.LCMSTransform)
形成線程轉儲,你可以看到線程#13
保持一個鎖後顯示器locked <0x00000000c463a080>
和線程#14
正在等待鎖定此顯示器waiting to lock <0x00000000c463a080>
。
如果陣列imgArray
只要你想它被寫入到圖像文件已經保存灰度信息,比你可以直接寫入數據(如已被haraldk提及。
而不是繪製圖像到另一個圖像
bGr.drawImage(image, 0, 0, null);
你直接寫光柵圖像信息
WritableRaster raster = bimage.getRaster();
raster.setDataElements(0, 0, canvasSize, canvasSize, imgArray);
執行ŧ imes在應用該更改後是
duration in sequence: 607
duration in threads: 134
您的機器有多少個物理處理器內核? – RAnders00
8,已經有兩個正在使用。 – Nate
你提到'bGr.darwImage'需要所有的時間?所以你的意思是不寫入磁盤會減慢速度?你是如何衡量這一點的?你能顯示線程部分的代碼嗎?由於圖像尺寸較小,尺寸爲512x512像素,因此繪製/寫入磁盤不應成爲瓶頸。 (現在已經刪除了與BufferedOutputStream相關的答案)。 – SubOptimal