결론부터 말하자면, Java Swing 컴포넌트는 스레드로부터 안전(thread-safe)하지 않습니다.
Swing 컴포넌트가 스레드로부터 안전하지 않은 이유
- 컴포넌트를 확장(상속)하는 작업을 단순화하기 위해서입니다. 모든 메서드에 동기화를 적용하면 서브클래싱이 복잡해지기 때문입니다.
- 락(lock)을 획득하고 해제하며 상태를 복원하는 과정에서 발생하는 오버헤드를 피하기 위해서입니다. 전체 컴포넌트에 대한 동기화는 성능 저하로 이어질 수 있습니다.
- 다만 일부 메서드는 예외적으로 멀티스레드 접근을 지원합니다. 대표적으로
JComponent클래스의 repaint(), revalidate(), invalidate() 메서드가 있으며, 이들은 어떤 스레드에서 호출해도 안전합니다.
이벤트 디스패치 스레드(EDT)란?
Swing 컴포넌트가 화면에 그려질 준비가 되면(페인팅 가능 상태가 되면), 해당 컴포넌트는 오직 이벤트 디스패치 스레드(Event Dispatch Thread, EDT)에서만 접근해야 합니다.
EDT는 paint(), update() 같은 콜백 메서드와 이벤트 리스너 인터페이스에 정의된 이벤트 핸들러 메서드들을 호출하는 스레드입니다. 스레드로부터 안전한 메서드만 어떤 스레드에서든 자유롭게 호출할 수 있으며, 대부분의 Swing 객체 메서드는 스레드 안전성을 보장하지 않기 때문에 반드시 단일 스레드인 EDT를 통해서만 호출해야 합니다.
예제 코드
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);
}
}
}코드 설명
위 예제에서 SwingUtilities.invokeLater() 메서드는 GUI 생성 작업을 초기 스레드(initial thread)의 작업이 모두 완료될 때까지 지연시킵니다. 이를 통해 GUI 생성이 반드시 EDT 내부에서 실행되도록 보장할 수 있습니다. 이것이 Swing 애플리케이션에서 권장되는 표준적인 GUI 초기화 패턴입니다.
실행 결과

정리
- Swing 컴포넌트는 기본적으로 스레드로부터 안전하지 않습니다.
- 모든 GUI 관련 작업은 EDT에서 수행해야 하며, 이를 위해
SwingUtilities.invokeLater()또는invokeAndWait()를 사용합니다. repaint(),revalidate(),invalidate()등 일부 메서드만 예외적으로 어느 스레드에서나 호출할 수 있습니다.- 시간이 오래 걸리는 작업은
SwingWorker를 활용해 백그라운드 스레드에서 처리하고, 결과 갱신만 EDT에서 수행하는 것이 좋습니다.