我的理解:
与JComponent.repaint()中Swing调用的大多数组件/操作不同,线程安全即使是从另一个线程(即不是从EDT)进行重新绘制请求,所以实际绘画仅在EDT中发生.以下代码片段演示了这一点.
与JComponent.repaint()中Swing调用的大多数组件/操作不同,线程安全即使是从另一个线程(即不是从EDT)进行重新绘制请求,所以实际绘画仅在EDT中发生.以下代码片段演示了这一点.
- 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()的情况下 – 绘画发生在调用它的同一个线程中.
考虑一个情况是,EDT正在更改组件的状态,另一个线程(从中调用paintImmediately())正在绘制相同的组件.
我的问题:
在paintImmediately()的情况下,Event Dispatcher Thread(EDT)和其他线程之间的同步处理如何?
解决方法
据我了解,当您调用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,h);
- } else {
- c.repaint(x,h);
- }
所以,除非这不是一个JComponent,否则你最终会调用_paintImmediately(),最后调用paint(Graphics),建议下面的栈跟踪(从我将在这篇文章结尾发贴的一段代码中获取):
- Thread [pool-1-thread-1] (Suspended)
- TestPaint$1.paint(Graphics) line: 23
- TestPaint$1(JComponent).paintToOffscreen(Graphics,int,int) line: 5221
- RepaintManager$PaintManager.paintDoubleBuffered(JComponent,Image,Graphics,int) line: 1482
- RepaintManager$PaintManager.paint(JComponent,JComponent,int) line: 1413
- RepaintManager.paint(JComponent,int) line: 1206
- TestPaint$1(JComponent)._paintImmediately(int,int) line: 5169
- TestPaint$1(JComponent).paintImmediately(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方法中修改一个变量的状态,添加一个睡眠以增加2个线程(EDT和另一个线程)之间的冲突风险,并且它看起来似乎没有两者之间的同步线程(System.err.println()不时输出为null).
现在我想知道为什么你需要立即执行一个paint.除非你阻止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,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();
- }
- });
- }
- }