[英]Is java.awt.GraphicsConfiguration thread-safe? What are the alternatives
我正在擴展javax.swing.JComponent
以顯示可變數量的圖塊,它們的大小均相同。
如果圖塊需要新外觀,則SwingWorker
的doInBackground()
渲染一個新的BufferedImage
。 在done()
,將存儲圖像並調用JComponent.repaint()
,以指示更新的區域和預期的延遲。 被重寫的JComponent.paintComponent()
將知道該怎么做。
磁貼的大小可以通過GUI進行更改。 Obvioulsy,它可能發生這樣的請求發生,而在SwingWorker
的StateValue
是PENDING
或STARTED
。
我認為支持cancel()
沒有多大意義; 它會使代碼復雜化,並且由於實際渲染不會花費很長時間,因此其影響將是最小的(如果工人必須等待的時間超過執行所需的時間,甚至會造成危害)。 相反,我想提高效率,並且如果同一圖塊存在PENDING
則EDT代碼不會啟動新的SwingWorker
。 然后,當doInBackground()
啟動時, SwingWorker
僅需要獲取最新設置,並檢查它是否真的應該將其結果存儲在done()
。
那么, SwingWorker
使用的BufferedImage
應該放在哪里? 這些似乎是選項:
paintComponent()
可以同時運行,所以必須始終為所有圖塊保留兩個最大大小的圖像(請考慮使用ViewPort
;動態解決方案只需要一秒鍾即可)可見圖塊實際所需尺寸的圖片)。 SwingWorker
時創建它。 缺點:必須提供最大大小,因為不知道一旦doInBackground()
所需的大小。 SwingWOrker
創建它。 問題:考慮到JComponent.paintComponent()
可能不得不經常調用drawImage()
,建議使用GraphicsConfiguration.createCompatibleImage()
創建此圖像。 這可能會破壞AWT的單線程性限制。 我更喜歡以下內容,但是由於GraphicsConfiguration
屬於AWT,並且實現取決於平台,因此這是安全的做法嗎?
...
final GraphicsConfiguration gc = this.getGraphicsConfiguration();
if ((obj.worker == null) ||
(obj.worker.getState() != SwingWorker.StateValue.PENDING)) {
obj.worker = new SwingWorker<BufferedImage, Void>() {
@Override public BufferedImage doInBackground() {
... // acquire size info via synchronised access
final BufferedImage img = gc.createCompatibleImage(...);
...
return img;
}
@Override public void done() {
if (obj.worker == this) {
obj.worker = null;
try { obj.image = this.get(); }
catch (Throwable t) { ... System.exit(1); }
Outer.this.requestTileRepaint(...);
}
}
};
obj.worker.execute();
}
...
澄清
查看上面的代碼,可能會爭辯說此解決方案沒有真正的多線程問題,因為GraphicsConfiguration
對象是在EDT上專門為此特定工作者創建的。 然而,
Component.getGraphicsConfiguration()
調用都返回相同的對象引用。 我以為最安全的方法是從EDT上的GraphicsConfiguration
提取所有相關信息,將其傳遞給工作程序,然后在其中使用合適的配置獲取new BufferedImage()
。 但是我在網上發現了一些提示,提示結果可能會導致drawImage()
出現令人驚訝的性能drawImage()
,這表明可能存在某些配置方面可能未明確涵蓋。
采納haraldK的想法,這是一個線程安全的解決方案,我已經在具有Java SE 1.6.0_26的Linux PC和具有Java SE 1.8.0_40的Windows 8.1筆記本上進行了測試。 (顯然,代碼可以改進,但超出了此問答)。
在兩個平台上,性能都可以通過處理器速度進行調整,並且兩個平台上的Transparency.BITMASK
都是通過BufferedImage.TYPE_CUSTOM
處理的,而Transparency.OPAQUE
和Transparency.TRANSLUCENT
使用特定的對應BufferedImage.TYPE_*
值。
同樣在兩個平台上,使用兩個new BufferedImage()
調用之間沒有明顯的性能差異,而GraphicsConfiguration.createCompatibleImage()
肯定要慢(30%到50%)。
整個機制由內部類提供。 外部類extend
s javax.swing.JComponent
因此該級別根本沒有同步。 但是, SwingWorker
是匿名內部類,並且部署圖像創建同步機制。
在經過測試的平台上, BufferedImage.getType()
的兩個類別之間的區別似乎是不必要的,但誰知道。
就我而言,innter類還包含SwingWorker
所需的其他信息。
private static final class WokerSync
{
private Object refImageMutex = new Object();
private BufferedImage refImageOpaque = null;
private BufferedImage refImageTranspMask = null;
private BufferedImage refImageTranslucent = null;
public void setRefImagesFromEDT(final GraphicsConfiguration grConf) {
if (grConf != null) {
synchronized(this.refImageMutex) {
this.refImageOpaque = grConf.createCompatibleImage(1, 1, Transparency.OPAQUE);
this.refImageTranspMask = grConf.createCompatibleImage(1, 1, Transparency.BITMASK);
this.refImageTranslucent = grConf.createCompatibleImage(1, 1, Transparency.TRANSLUCENT);
}
}
}
private BufferedImage getCompatibleImage(final BufferedImage refImage, final int width, final int height) {
BufferedImage img = null;
if (refImage != null) {
final int grType = refImage.getType();
if (grType == BufferedImage.TYPE_CUSTOM) {
final ColorModel cm = refImage.getColorModel();
final WritableRaster wr = cm.createCompatibleWritableRaster(width, height);
final String[] ps = refImage.getPropertyNames();
final int pl = (ps == null) ? 0 : ps.length;
final Hashtable<String,Object> ph = new Hashtable<String,Object>(pl);
for (int pi=0; pi<pl; pi++) {
ph.put(ps[pi], refImage.getProperty(ps[pi]));
}
img = new BufferedImage(cm, wr, cm.isAlphaPremultiplied(), ph);
} else {
img = new BufferedImage(width, height, grType);
}
}
return img;
}
public BufferedImage getCompatibleImageOpaque(final int width, final int height) {
BufferedImage img = null;
synchronized(this.refImageMutex) {
img = this.getCompatibleImage(this.refImageOpaque, width, height);
}
return img;
}
public BufferedImage getCompatibleImageTranspMask(final int width, final int height) {
BufferedImage img = null;
synchronized(this.refImageMutex) {
img = this.getCompatibleImage(this.refImageTranspMask, width, height);
}
return img;
}
public BufferedImage getCompatibleImageTranslucent(final int width, final int height) {
BufferedImage img = null;
synchronized(this.refImageMutex) {
img = this.getCompatibleImage(this.refImageTranslucent, width, height);
}
return img;
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.