0

Series Java Swing Bài 2: Event Dispatch Thread (EDT) - Quy tắc sống còn khi lập trình UI.

Khi làm việc với Java Swing, có một khái niệm quan trọng hơn cả việc học cách thiết kế giao diện: Event Dispatch Thread (EDT). Nếu bạn không hiểu EDT, ứng dụng của bạn sớm muộn cũng sẽ gặp tình trạng "Not Responding" (đơ giật) hoặc xuất hiện các lỗi đồ họa kỳ lạ mà không báo lỗi ở console.

1. EDT là gì và tại sao Swing lại thiết kế như vậy?

Event Dispatch Thread (EDT) là một luồng (thread) chạy ngầm đặc biệt và duy nhất trong kiến trúc Swing. Nhiệm vụ duy nhất của luồng này là:

  • Vẽ và cập nhật giao diện (Render UI).

  • Lắng nghe và xử lý các sự kiện từ người dùng (Click chuột, gõ phím, di chuyển chuột).

Tại sao Swing chỉ dùng 1 luồng duy nhất cho UI? Hầu hết các thành phần của Swing (JButton, JLabel, JTable...) đều không Thread-safe (không an toàn khi chạy đa luồng). Nếu bạn cho phép nhiều luồng (thread) cùng lúc can thiệp vào một nút bấm — ví dụ: luồng A đổi chữ, luồng B đổi màu, luồng C ẩn nút đi — sẽ xảy ra hiện tượng Race Condition. Giao diện sẽ bị vẽ lỗi, nhấp nháy hoặc crash toàn bộ chương trình.

Giải pháp của những nhà thiết kế Swing là đưa mọi thao tác liên quan đến UI vào một luồng duy nhất (EDT) để xử lý tuần tự theo dạng hàng đợi (Queue).

2. Quy tắc sống còn (The Golden Rule)

Bất kỳ đoạn code nào tạo ra, thay đổi, hoặc truy vấn trạng thái của các Component Swing đều PHẢI được thực thi trên Event Dispatch Thread (EDT).

Điều này có nghĩa là ngay từ hàm main() của Java (vốn chạy trên luồng Main-Thread), bạn không được phép khởi tạo JFrame trực tiếp mà phải đẩy việc đó sang cho EDT.

3. Cách tuân thủ quy tắc: SwingUtilities.invokeLater

Để đẩy một khối lệnh sang cho EDT xử lý, Java cung cấp phương thức SwingUtilities.invokeLater().

Java

import javax.swing.*;

public class MainApp {
    public static void main(String[] args) {
        // LUỒNG CHÍNH (Main-Thread): Không nên thao tác UI ở đây
        System.out.println("Luồng hiện tại: " + Thread.currentThread().getName());

        // Đẩy công việc khởi tạo UI sang cho EDT
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                // ĐOẠN CODE NÀY CHẠY TRÊN EDT
                System.out.println("Luồng UI: " + Thread.currentThread().getName());
                JFrame frame = new JFrame("Minh họa EDT");
                frame.setSize(300, 200);
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setVisible(true);
            }
        });
    }
}

Bạn cũng có thể viết ngắn gọn lại bằng Lambda Expression (Java 8+):

Java

SwingUtilities.invokeLater(() -> {
    new MainAppFrame().setVisible(true);
});

4. Lỗi kinh điển nhất: "Đơ" giao diện (Freezing UI)

Vì EDT là luồng duy nhất chịu trách nhiệm vẽ màn hình và nhận click chuột, điều gì xảy ra nếu bạn bắt EDT làm một việc tốn thời gian?

Giả sử khi người dùng bấm nút "Lấy dữ liệu", bạn gọi thẳng vào Database hoặc gọi 1 API tốn 5 giây để phản hồi. Trong suốt 5 giây đó, EDT bị "khóa" (block) tại dòng code gọi API. Hệ quả:

  1. Giao diện bị đóng băng, không thể vẽ lại.

  2. Các nút bấm khác không thể click.

  3. Người dùng kéo cửa sổ đi chỗ khác sẽ để lại vệt đen hoặc trắng bóc.

  4. Hệ điều hành hiện thông báo: "Ứng dụng Not Responding".

Mô phỏng lỗi chặn EDT:

Java

JButton btnLoad = new JButton("Tải dữ liệu");
btnLoad.addActionListener(e -> {
    // Sự kiện click chuột chạy trên EDT.
    // Nếu bạn sleep hoặc query Database ở đây, toàn bộ UI sẽ bị đơ!
    try {
        Thread.sleep(5000); // Giả lập việc nặng mất 5 giây
    } catch (InterruptedException ex) {
        ex.printStackTrace();
    }
    btnLoad.setText("Đã tải xong!"); 
});

Cách giải quyết: Với các tác vụ nặng (truy vấn DB, đọc file, gọi API), bạn tuyệt đối không được viết trực tiếp bên trong sự kiện click nút bấm. Bạn phải tách nó ra một luồng chạy nền (Background Thread). Trong Swing, công cụ hoàn hảo nhất để làm việc này là SwingWorker (Chúng ta sẽ tìm hiểu sâu về nó ở Bài 42 & 43).


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí