Hỏi về đa luồng trong java
Chào các bạn, chomình hỏi chút về đa luồng trong java. Mình có 1 giao diện swing, trên đó có duy nhất 1 nút Restart, khi bấm nút thì thực hiện việc setText cho 1 label. Bên cạnh đó mình có 1 luồng in ra số từ 1 đến 10.000. như sau
public class ThreadCheckVer implements Runnable{
@Override
public void run() {
for (int i = 0; i < 1000000; i++) {
System.out.println(i);
}
} }
Khi chạy giao diện swing mình đồng thời cho thread này chạy
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
restartDemo frame = new restartDemo();
frame.setVisible(true);
//setdata();
tcv = new Thread(new ThreadCheckVer());
tcv.start();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}`
Sự kiên cho nút trên giao diện swing
`btnRestart.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
int opt = JOptionPane.showConfirmDialog(null,"Are you sure?", "Confirm", JOptionPane.YES_NO_OPTION);
if (opt == JOptionPane.YES_OPTION) {
lblstatus.setText("Restarting...");
try {
tcv.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
lblstatus.setText("Restarting...");
}
}
});`
Vấn đề ở đây là mình mong muốn khi bấm nút thì lblstatus sẽ hiện “Restarting…” trong khi chờ thread in số xong việc, nhưng thực tế thì nó đang chạy ngược lại, in xong số rồi mới setText cho lblstatus.
Bạn nào có thể giải thích giúp mình vì sao lại như vậy không? Tks a lot.
Rất đơn giản vì Swing chạy trên 1 thread gọi là Event Dispatching Thread (vẽ vời, init các UI về đồ họa và là thread chạy nặng nhất). Khi bạn gọi lệnh tcv.join, thread EDT sẽ phải đợi thread này kết thúc thì nó mới chạy tiếp cập nhật lại giao diện (do đó, nó sẽ block cái thread EDT và app trở thành non-reponsive).
Bỏ cái tcv.join đi là app sẽ reponsive ngay.
Tuy nhiên, mình chưa hiểu context nên không biết bạn có cần nghe xem thread đếm số đã xong chưa? Nếu cần nghe thì bạn lại nhét vào EventQueue và nghe (lúc đó app vẫn reponsive được, khi thread tcv xong, nó sẽ gửi event sang cho EDT để cập nhật lại)
Kịch bản của mình như này: Giao diện swing và thread tcv chạy lên đồng thời -> Bấm nút Restart để khởi động lại app -> Sau khi bấm nút thì app sẽ chờ cho thread tcv chạy xong mới restart, đồng thời ngay sau thời điểm bấm nút thì label sẽ đc setText “Restarting…” -> Sau khi thread tcv xong việc thì khởi động lại app.
Nếu bỏ tcv.join() thì không được vì app khởi động lại trong khi thread tcv vẫn đang chạy, mình muốn mọi thread con phải xong hết mới khởi động lại.
Như vậy thì nếu dùng tcv.join() thì hoặc thread main hoặc thread EDT sẽ bị block không vẽ lại được giao diện, bạn có phương án nào để xử lý việc này không?
Hi, giải pháp thì có 1 số:
Mình có code thử cách 1 (chưa work vì Frame được init trên EDT) nên sử dụng cách 2 (mình dùng luôn SwingWorker cho nhanh ^^). Bạn thử tham khảo xem có hướng gì được không?
GitHub
laisotrym/POC
Contribute to laisotrym/POC development by creating an account on GitHub.
Để mình thử xem, tiện đây chomình hỏi thêm cái này chút: Vẫn là code trên, thay vì setText cho label, mình đưa ra 1 box confirm (JOptionPane.showConfirmDialog(…)) thì nó lại hiện ra ngay mà tại sao cái setText lại chờ đến khi thread xong mới có hiệu lực (vì hiện tại lúc này thread EDT đang blocked)
Thanks Hoàng nhé, mình thử dùng SwingWorker đã giải quyết được rồi. Tuy nhiên mình chưa hiểu rõ lắm, khi dùng swingworker như vậy thì nó chạy trên luồng nào? EDT hay main?
Nó chạy trên luồng riêng theo design pattern Observer / Message Bus đó ^^
Còn câu hỏi của bạn là tại sao Dialog hiện ra ngay thì mình cũng chưa trả lời được. ^^
Bình thường mình hiểu Dialog trong Java nó hoạt động thế này:
(Join là cơ chế xếp hàng.Ví dụ nếu bạn có 2 thread t1, t2 cùng join với EDT thì t1,t2 này chạy // với nhau, nhưng EDT buộc phải chờ cả 2 thằng này thì mới chạy tiếp được. Thường thì khi lập trình với Thread, nên dùng cơ chế wait/notify để làm synchronize hơn để tránh bị block)
Thanks vì những chia sẻ của bạn.