Lea*_*ner 11 java swing multithreading jcomponent
我的理解: 与Swing中的大多数组件/操作不同,对JComponent.repaint()的调用是线程安全的,即虽然重新绘制请求来自另一个线程(即不是来自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);
}
}
Run Code Online (Sandbox Code Playgroud)
从输出中可以看出,当调用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,y,w,h);
} else {
c.repaint(x,y,w,h);
}
Run Code Online (Sandbox Code Playgroud)
因此,除非这不是a,否则JComponent你最终会调用_paintImmediately()最终调用,paint(Graphics)如下所示的堆栈跟踪(从我将在本文末尾发布的一段代码中捕获):
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
Run Code Online (Sandbox Code Playgroud)
但是如果你试图同时调用repaint()(从另一个Thread),你会看到两个同时运行(我尝试使用debuger踩代码并且绘画从未停止在另一个Thread中出现)似乎在Java代码级别,没有太多同步(至少我找不到任何东西).因此,如果您最终修改了EDT中的组件状态,我相信结果是非常不可预测的,您应该通过各种方式避免这种情况.
为了说明我的观点,我尝试修改paint方法中变量的状态,添加一个sleep来增加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, 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();
}
});
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
6858 次 |
| 最近记录: |