Không, các thành phần Java Swing không thread-safe.
Tại sao Swing không thread-safe
- Lý do chính: để đơn giản hóa việc mở rộng (extend) các thành phần.
- Lý do thứ hai: chi phí (overhead) của việc获取 và giải phóng khóa (locks) cũng như khôi phục trạng thái.
- Một số phương thức của thành phần Swing hỗ trợ đa luồng như repaint(), revalidate(), invalidate() của lớp JComponent.
Event Dispatch Thread (EDT)
Các thành phần Swing chỉ có thể truy cập từ Event Dispatch Thread (EDT) sau khi thành phần sẵn sàng vẽ trên màn hình. EDT là luồng gọi các phương thức callback như paint(), update() ngoài các phương thức xử lý sự kiện định nghĩa trong interface Event Listener. Chỉ các phương thức thread-safe mới có thể gọi an toàn từ bất kỳ luồng nào. Vì hầu hết phương thức đối tượng Swing không thread-safe, chúng chỉ được gọi từ một luồng duy nhất: EDT.
Ví dụ minh họa
import javax.swing.*;
import java.awt.Dimension;
import java.awt.event.*;
public class EDTTest extends JPanel implements ActionListener {
private static EDTTest myPanel;
private static JFrame myFrame;
public EDTTest() { super(); }
public Dimension getPreferredSize() {
return new Dimension(500, 425);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
private static void createAndShowGUI() {
myFrame = new JFrame("EDT Program");
myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myFrame.setLocationRelativeTo(null);
myPanel = new EDTTest();
myFrame.add(myPanel);
initMenu();
myFrame.setVisible(true);
}
private static void initMenu() {
JMenuBar menuBar = new JMenuBar();
myFrame.setJMenuBar(menuBar);
JMenu file = new JMenu("File");
JMenuItem quit = new JMenuItem("Quit");
quit.addActionListener(myPanel);
file.add(quit);
menuBar.add(file);
}
public void actionPerformed(ActionEvent ae) {
String choice = ae.getActionCommand();
if (choice.equals("Quit")) {
System.exit(0);
}
}
}
Trong ví dụ, SwingUtilities.invokeLater() trì hoãn tác vụ tạo GUI cho đến khi các tác vụ của luồng ban đầu hoàn thành, đảm bảo tạo GUI diễn ra bên trong EDT.
Kết quả đầu ra
Cửa sổ có thanh menu "File" với mục "Quit". Khi chọn Quit, ứng dụng thoát. Toàn bộ tạo GUI chạy trên EDT.
Quy tắc vàng
- Tạo và sửa đổi GUI chỉ trên EDT (dùng
SwingUtilities.invokeLater()hoặcinvokeAndWait()). - Các tác vụ chạy lâu (I/O, tính toán nặng) không được chạy trên EDT (sẽ làm GUI bị đơ) – dùng
SwingWorker. repaint(),revalidate()an toàn từ bất kỳ luồng nào.