簡體   English   中英

java.awt.GraphicsConfiguration線程安全嗎? 有哪些選擇

[英]Is java.awt.GraphicsConfiguration thread-safe? What are the alternatives

我正在擴展javax.swing.JComponent以顯示可變數量的圖塊,它們的大小均相同。

如果圖塊需要新外觀,則SwingWorkerdoInBackground()渲染一個新的BufferedImage done() ,將存儲圖像並調用JComponent.repaint() ,以指示更新的區域和預期的延遲。 被重寫的JComponent.paintComponent()將知道該怎么做。

磁貼的大小可以通過GUI進行更改。 Obvioulsy,它可能發生這樣的請求發生,而在SwingWorkerStateValuePENDINGSTARTED

我認為支持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.OPAQUETransparency.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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM