2012-12-25 9 views
11

私の理解: JComponent.repaint()へのSwing呼び出しのコンポーネント/操作のほとんどはスレッドセーフです。つまり、別のスレッド(つまりEDTではなく)から再描画要求が行われても、実際のペイントはEDTのみ。下記のコードスニペットはこれを示しています。これは問題 - 再描画が()にかかわらずそのスレッドから、それが呼び出されていないのが呼び出されたとき、絵がEDTで行われることが知られている出力から JComponent.paintImmediately()はJava Swingでどのように動作しますか?

public class PaintingDemo { 

    public static void main(String[] args) { 
     final JFrame frame = new JFrame(); 
     final JPanel p = new MyPanel(); 
     SwingUtilities.invokeLater(new Runnable() { 
      @Override 
      public void run() { 
       frame.add(p, BorderLayout.CENTER); 
       frame.setSize(200, 200); 
       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
       frame.setVisible(true); 
      } 
     }); 
     new Thread("MyThread") { 
      public void run() { 
       while (true) { 
       // Below statements are important to show the difference 
        p.repaint(); 
        p.paintImmediately(p.getBounds()); 
        try { 
         Thread.sleep(1000); 
        } catch(Exception e) {} 
       } 
      } 
     }.start(); 
    } 

} 

class MyPanel extends JPanel { 
    @Override 
    public void paint(Graphics g) { 
     System.out.println("paint() called in "+ Thread.currentThread().getName()); 
     super.paint(g); 
    } 
} 

。しかし、paintImmediately()の場合、ペイントはそれが呼び出された同じスレッドで行われます。

EDTがコンポーネントの状態を変更し、別のスレッド(paintImmediately()が呼び出された)が同じコンポーネントをペイントしている場合を考えてみましょう。

私の質問:paintImmediately()の場合 は、どのようにイベントディスパッチャスレッド(EDT)と他のスレッド間の同期が処理されますか?これはJComponentない場合を除き、あなたはスタックトレースが示唆するようpaint(Graphics)を呼び出して終了_paintImmediately()を呼び出してしまう、だから、

 Component c = this; 
     Component parent; 

     if(!isShowing()) { 
      return; 
     } 

     JComponent paintingOigin = SwingUtilities.getPaintingOrigin(this); 
     if (paintingOigin != null) { 
      Rectangle rectangle = SwingUtilities.convertRectangle(
        c, new Rectangle(x, y, w, h), paintingOigin); 
      paintingOigin.paintImmediately(rectangle.x, rectangle.y, rectangle.width, rectangle.height); 
      return; 
     } 

     while(!c.isOpaque()) { 
      parent = c.getParent(); 
      if(parent != null) { 
       x += c.getX(); 
       y += c.getY(); 
       c = parent; 
      } else { 
       break; 
      } 

      if(!(c instanceof JComponent)) { 
       break; 
      } 
    } 
    if(c instanceof JComponent) { 
     ((JComponent)c)._paintImmediately(x,y,w,h); 
    } else { 
     c.repaint(x,y,w,h); 
    } 

:あなたはpaintImmediatelyを呼び出すとき

答えて

5

私の理解するには、次のコードを呼び出します

Thread [pool-1-thread-1] (Suspended)  
    TestPaint$1.paint(Graphics) line: 23  
    TestPaint$1(JComponent).paintToOffscreen(Graphics, int, int, int, int, int, int) line: 5221 
    RepaintManager$PaintManager.paintDoubleBuffered(JComponent, Image, Graphics, int, int, int, int) line: 1482 
    RepaintManager$PaintManager.paint(JComponent, JComponent, Graphics, int, int, int, int) line: 1413 
    RepaintManager.paint(JComponent, JComponent, Graphics, int, int, int, int) line: 1206 
    TestPaint$1(JComponent)._paintImmediately(int, int, int, int) line: 5169  
    TestPaint$1(JComponent).paintImmediately(int, int, int, int) line: 4980 
    TestPaint$1(JComponent).paintImmediately(Rectangle) line: 4992 
    TestPaint$3.run() line: 50 
    ThreadPoolExecutor.runWorker(ThreadPoolExecutor$Worker) line: 1110 
    ThreadPoolExecutor$Worker.run() line: 603 
    Thread.run() line: 722 

しかし、あなたはまた、(別のスレッドから)同時にrepaint()をコールしようとした場合、あなたは両方のことを参照してください(私はこの記事の最後に掲載しますコードの一部からキャプチャ)以下同時に(私はデバッガでコードにステップインしようとしていて、他のスレッドで描画が停止することはありませんでした)、Javaコードレベルでは、同期はあまりありません(少なくとも私は何も見つけられませんでした)。したがって、EDTでコンポーネントの状態を変更すると、結果はまったく予測できず、このような状況を避けるべきだと私は信じています。

paintメソッド内の変数の状態を変更しようとしましたが、sleepを追加して2つのスレッド(EDTと他のスレッド)の衝突の危険性を増やしました。 2つのスレッド(System.err.println()が時々出力されるnull)間の同期。

今すぐpaintImmediatelyを実行する必要があるのはなぜかと思います。あなたがEDTをブロックしていない限り、そのようなことを実行する有効な理由はあまりありません。

以下は、私がこれらのものをテストするために使用したコードです(質問に投稿されたものとかなり近い)。このコードは、何が起きているのかを理解しようとするものであり、適切な絵画を実行する方法や優れたSwingの練習を示すものではありません。

import java.awt.Color; 
import java.awt.Graphics; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.util.Random; 
import java.util.concurrent.Executors; 

import javax.swing.JFrame; 
import javax.swing.JPanel; 
import javax.swing.SwingUtilities; 
import javax.swing.Timer; 

public class TestPaint { 

    protected void initUI() { 
     JFrame frame = new JFrame(); 
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     frame.setTitle(TestPaint.class.getSimpleName()); 
     final Random rand = new Random(); 
     final JPanel comp = new JPanel() { 
      private String value; 

      @Override 
      public void paint(Graphics g) { 
       value = "hello"; 
       super.paint(g); 
       try { 
        Thread.sleep(20); 
       } catch (InterruptedException e) { 
        // TODO Auto-generated catch block 
        e.printStackTrace(); 
       } 
       g.setColor(new Color(rand.nextInt(256), rand.nextInt(256), rand.nextInt(256))); 
       g.fillRect(0, 0, getWidth(), getHeight()); 
       if (SwingUtilities.isEventDispatchThread()) { 
        System.err.println("Painting in the EDT " + getValue()); 
       } else { 
        System.err.println("Not painting in EDT " + getValue()); 
       } 
       value = null; 
      } 

      public String getValue() { 
       return value; 
      } 
     }; 
     frame.add(comp); 
     frame.setSize(400, 400); 
     frame.setLocationRelativeTo(null); 
     frame.setVisible(true); 
     Timer t = new Timer(1, new ActionListener() { 

      @Override 
      public void actionPerformed(ActionEvent e) { 
       comp.repaint(); 
      } 
     }); 
     t.start(); 
     Executors.newSingleThreadExecutor().execute(new Runnable() { 

      @Override 
      public void run() { 
       while (true) { 
        comp.paintImmediately(comp.getBounds()); 
       } 
      } 
     }); 
    } 

    public static void main(String[] args) { 
     SwingUtilities.invokeLater(new Runnable() { 
      @Override 
      public void run() { 
       new TestPaint().initUI(); 
      } 
     }); 
    } 

} 
+0

+1あなたはそれを釘付けにしました。 'JComponent'ドキュメントがなぜ' paintImmediately() 'と' repaint(..) 'を参照しているのかというだけでは不思議です。あなたのスニペットで、 'JComponent'の' paint(..) 'をオーバーライドすることは決してありません。どうして' paintComponent'を使用しなかったのですか(まだ結果を再現しているようです)?そしてもう一つの疑問はなぜ 'sleep(int milis) 'を増やして衝突を増加させないのでしょうか?また、EDT以外のスレッドから 'paintImmediately'を呼び出すのはなぜですか(なぜならEDTで呼び出されるのはnullが発生しないためです)。 –

+0

自分自身で試してみて、あなたの研究を共有してくれてありがとう@Guillaume。私はpaintImmediately()を使用する理由はそれほど多くないことに同意しますが、私は単にそれがどのように機能するのかを理解しようとしていました。 javadocはこう言っています。 "このメソッドは、現在のイベントがディスパッチされている間にディスプレイを更新する必要がある場合に便利です。"また、このメソッドは公開されており、その警告についてはどこにも言及されていない警告は絶対にありません。少なくともjavadocは誤解を招きます。今私はpaintImmediately()がEDT内で呼び出される必要があり、repaint()とは異なりスレッドセーフではないことを理解しています。 – Learner

+0

@DavidKroukamp 'paintComponent'ではなく' paint'をオーバーライドする正当な理由はなく、 'paintImmediately'が最終的に' paint'を呼び出すのを見て、スタックコールの 'paintComponent'への追加呼び出しを追加することは不要でした観察された結果は同じままである。だから、情報から「ノイズ」を取り除くだけではなく、スニペットの前に述べたように、スイングを使う正しい方法を示していません。乾杯;-) –

関連する問題