JComponent.paintImmediately()如何在Java Swing中工作? [英] How does JComponent.paintImmediately() work in Java Swing?

查看:1174
本文介绍了JComponent.paintImmediately()如何在Java Swing中工作?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的理解:
与Swing中的大多数组件/操作不同,对JComponent.repaint()的调用是线程安全的,即重绘请求是从另一个线程发出的(即不是来自EDT),实际绘画​​仅在EDT中进行。下面的代码片段演示了这一点。

My understanding: Unlike most of the components/operations in Swing call to JComponent.repaint() is thread-safe i.e. though a repaint request is made from another thread (i.e. not from EDT), the actual painting happens in EDT only. Below code snippet demonstrates this.

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);
    }
}

从输出中可以看出,绘画是在调用repaint()时的EDT,无论调用哪个线程 - 所以没有问题。但是,在paintImmediately()的情况下 - 绘制发生在调用它的同一个线程中。

From the output it is known that painting is done in EDT when repaint() is invoked irrespective of from which thread it is called - so no issues. But, in the case of paintImmediately() - painting happens in the same thread from which it is called.

考虑EDT正在改变组件状态的情况另一个线程(从中调用paintImmediately())绘制相同的组件。

Consider a case where EDT is changing the state of a component and another thread (from which paintImmediately() is invoked) is painting the same component.

我的问题:
如果是paintImmediately (),如何处理Event Dispatcher Thread(EDT)和其他线程之间的同步?

My Question: In case of paintImmediately(), how is synchronization between Event Dispatcher Thread (EDT) and other threads handled?

推荐答案

据我所知,当你打电话时paintImmediately,您调用以下代码:

To my understanding, when you call paintImmediately, you call the following code:

        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);
    }

所以,除非这不是 JComponent ,你最终调用 _paintImmediately()最终调用 paint(Graphics),如建议的那样下面的堆栈跟踪(从我将在本文末尾发布的一段代码中捕获):

So, unless this is not a JComponent, you end up calling _paintImmediately() which ends up calling paint(Graphics) as suggests the stack trace below (captured from a piece of code I will post at the end of this post):

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(),你会看到两个同时运行(我试着踩到debuger和绘画的代码永远不会停止在另一个Thread中出现)似乎在Java代码级别,没有太多的同步(至少我找不到任何东西)。因此,如果您最终修改了EDT中的组件状态,我相信结果是非常难以预测的,您应该尽一切可能避免这种情况。

But if you try to also call repaint() simultaneously (from another Thread), you see that both run at the same time (I tried stepping in the code with debuger and painting never stopped occuring in the other Thread) it seems that at the Java code level, there is not much synchronization (at least I could not spot anything). So if you end up modifying the component state in the EDT, I believe that results are quite unpredictable and you should avoid such situation by all means.

只是为了说明我的观点,我尝试在 paint 方法中修改变量的状态,添加 sleep 以增加之间发生冲突的风险两个线程(EDT和另一个)看起来两个线程之间没有同步( System.err.println()输出 null 不时)。

Just to illustrate my point, I tried modifying the state of a variable within the paint method, add a sleep to increase the risk of collisions between the 2 Threads (EDT and the other) and it obvisouly seems that there is no synchronization between the two Threads (the System.err.println() outputted null from time to time).

现在我想知道为什么你需要立即执行paint。除非你阻止了EDT,否则没有太多合理的理由来执行这样的事情。

Now I would wonder why you need to perform a paintImmediately. Unless you are blocking the EDT, there is not so many valid reasons to perform such thing.

下面是我用来测试这些东西的代码(非常接近发布的代码)在问题中)。代码只是试图了解正在发生的事情,而不是展示如何进行正确的绘画或任何良好的Swing练习。

Below is the code I used to test those things (pretty close the one posted in the question). The code is only meant to try to understand what is going on, not to show how to perform proper painting nor any good Swing practice.

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();
            }
        });
    }

}

这篇关于JComponent.paintImmediately()如何在Java Swing中工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆