简体   繁体   English

GUI Action Listener JAVA中的线程休眠

[英]Thread sleep in GUI Action Listener JAVA

I have a problem with Thread sleep. 我有线程睡眠问题。 I'm coding easy alarm clock, which will set up 10 min then it will count down in label(lab1). 我正在编写简单的闹钟,它将设置10分钟,然后将在label(lab1)中倒数。 When I press button, it freezes and thats all. 当我按下按钮时,它冻结,仅此而已。 Where is problem? 问题在哪里? I cant work with threads as master so far thats the reason why it is linearly. 到目前为止,我还不能将线程作为主线程使用,这就是线性的原因。 And can i have a question? 我可以问一个问题吗? How do I execute shell command? 如何执行shell命令? (Sorry, but it wants to make this longer. But it interest me too); (对不起,但是它想要延长它的时间。但是我也很感兴趣);

public class Budik extends JFrame{
    private JLabel lab1,lab2;
    private JButton butt1,butt2,butt3;
    private JTextField t1,t2;
    private JSpinner spinner;
    private JCheckBox cx1,cx2;
    private JList list;
    private JPanel p1,p2,p3,p4,p5;
    private JPasswordField pass1,pass2;
    public static void main(String[] args)
    {
        Budik okno = new Budik();
        okno.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        okno.setVisible(true);
        okno.pack();
        okno.setLocationRelativeTo(null);
        okno.setResizable(false);
    }
    public Budik() {
       super("Countdown");       
       setLayout(new BorderLayout());
       /////////////////////////////////////////////////////////////////
       // p1
       GridLayout el = new GridLayout(2,1);
       p1 = new JPanel(el);
       add(p1,BorderLayout.NORTH);
       lab1 = new JLabel("Welcome",SwingConstants.CENTER); //centr labelu
       lab2 = new JLabel("Created by WajFaj",SwingConstants.CENTER); //centr labelu
       lab1.setFont(new Font("Serif", Font.PLAIN, 36)); //velikost fontu
       p1.add(lab1);
       p1.add(lab2);
       //p2
       p2 = new JPanel();
       add(p2,BorderLayout.WEST);
       butt1 = new JButton("START");
       p2.add(butt1);

       //p3
       p3 = new JPanel();
       add(p3,BorderLayout.CENTER);
       butt2 = new JButton("STOP");
       p3.add(butt2);



       //p4
       p4 = new JPanel();
       add(p4,BorderLayout.EAST);
       butt3 = new JButton("RESET");
       p4.add(butt3);

       //p5
       p5 = new JPanel();
       add(p5,BorderLayout.SOUTH);





          butt1.addActionListener(new ActionListener(){
             public void actionPerformed(ActionEvent evt){


               for(int i=600; i>0;i-- ){ 
                try{
                       Thread.sleep(1000);
                       String cc = ""+i;
                       lab1.setText(cc);
                }
                catch(InterruptedException e){
                    System.out.println("Chyba!");
                }
               }
             }
         });

I am going to drop here of how i would do it (furthermore how it should be). 我将在这里介绍我将如何做(此外应该如何做)。 Comments inside the code. 代码内的注释。

package test;

import java.awt.BorderLayout;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.concurrent.TimeUnit;

import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JSpinner;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

public class Budik extends JFrame {
    private static final int SLEEP_MINUTES = 10;
    private long endTime;
    private JLabel lab1, lab2;
    private JButton butt1, butt2, butt3;
    private JTextField t1, t2;
    private JSpinner spinner;
    private JCheckBox cx1, cx2;
    private JList list;
    private JPanel p1, p2, p3, p4, p5;
    private JPasswordField pass1, pass2;
    private Timer timer;

    public static void main(String[] args) {
        //All swing applications must run on EDT.
        //Take a look https://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html
        SwingUtilities.invokeLater(()->{
            Budik okno = new Budik();
            okno.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            okno.pack();
            okno.setLocationRelativeTo(null);
            okno.setResizable(false);
            okno.setVisible(true); //First pack, then setVisible(), recommended.
        });
    }

    public Budik() {
        super("Countdown");
        setLayout(new BorderLayout());
        /////////////////////////////////////////////////////////////////
        // p1
        GridLayout el = new GridLayout(2, 1);
        p1 = new JPanel(el);
        add(p1, BorderLayout.NORTH);
        lab1 = new JLabel("Welcome", SwingConstants.CENTER); // centr labelu
        lab2 = new JLabel("Created by WajFaj", SwingConstants.CENTER); // centr labelu
        lab1.setFont(new Font("Serif", Font.PLAIN, 36)); // velikost fontu
        p1.add(lab1);
        p1.add(lab2);
        // p2
        p2 = new JPanel();
        add(p2, BorderLayout.WEST);
        butt1 = new JButton("START");
        p2.add(butt1);

        // p3
        p3 = new JPanel();
        add(p3, BorderLayout.CENTER);
        butt2 = new JButton("STOP");
        p3.add(butt2);

        // p4
        p4 = new JPanel();
        add(p4, BorderLayout.EAST);
        butt3 = new JButton("RESET");
        p4.add(butt3);

        // p5
        p5 = new JPanel();
        add(p5, BorderLayout.SOUTH);

        butt1.addActionListener(e->{
            endTime = System.currentTimeMillis()+TimeUnit.MINUTES.toMillis(SLEEP_MINUTES); //define the end time
            timer.start(); //Start the timer (in stop button, just add timer.stop())
        });
        //Initialize timer.
        timer = new Timer(1000, e->{ //Loop every 1000ms a.k.a 1second
            long millisBeforeEnd = endTime - System.currentTimeMillis();
            if (millisBeforeEnd < 0) {
                timer.stop();
                lab1.setText("Welcome"); //Restore the text to your label, or write whatever you want.
                return;
            }
            long secsBeforeEnd  = TimeUnit.MILLISECONDS.toSeconds(millisBeforeEnd); //Convert the millis to seconds.
            lab1.setText(secsBeforeEnd+"");
            //If you want some kind of pretty print, uncomment the code below.
            /*
            int hours = (int) TimeUnit.MILLISECONDS.toHours(millisBeforeEnd);
            int mins = (int) (TimeUnit.MILLISECONDS.toMinutes(millisBeforeEnd) - hours * 60);
            int secs = (int) (TimeUnit.MILLISECONDS.toSeconds(millisBeforeEnd) - mins * 60 - hours * 60 * 60);
            lab1.setText(String.format("%02d:%02d:%02d", hours, mins, secs));
            */
        });
        timer.setInitialDelay(0); //Don't loose the first second.
    }
}

You really don't want your program to "wait" until the timer goes off, because then the program will be unable to do anything else, including updating the graphics interface as you found out. 您确实不希望您的程序在计时器关闭之前“等待”,因为这样程序将无法执行其他任何操作,包括更新发现的图形界面。

The best solution is to create a javax.swing.Timer object, and then set timer to notify your system, when the needed time have passed. 最好的解决方案是创建一个javax.swing.Timer对象,然后设置计时器以在经过所需时间后通知您的系统。 See https://docs.oracle.com/javase/tutorial/uiswing/misc/timer.html for details about creating a timer. 有关创建计时器的详细信息,请参见https://docs.oracle.com/javase/tutorial/uiswing/misc/timer.html

Part of the issue is that you're calling Thread.sleep() 问题的一部分是您在调用Thread.sleep()

That is putting the Main thread to sleep so you're entire application stops executing. 那会使Main线程进入睡眠状态,因此整个应用程序将停止执行。

 for(int i=600; i>0;
    try{
       Thread.sleep(1000);
       String cc = ""+i;
       lab1.setText(cc);
    }

For your application sounds like you need to make a separate thread to run that will then wake up and actually run the alarm. 对于您的应用程序来说,您需要创建一个单独的线程来运行,然后将其唤醒并实际运行警报。

Maybe try something like this: 也许尝试这样的事情:

Thread t = new Thread(() -> {
        for(int i=600; i>0;i-- ){
            try{
                Thread.sleep(1000);
                String cc = ""+i;
                lab1.setText(cc);
            }
            catch(InterruptedException e){
                System.out.println("Chyba!");
            }
        }
    });
    t.start();

The call to Thread.sleep() should put the sub thread to sleep and not effect the rest of your application. 调用Thread.sleep()应该使子线程进入睡眠状态,并且不会影响应用程序的其余部分。

Please note I'm using arrow notation ( -> ) for the thread. 请注意,我在线程中使用箭头符号( -> )。 You don't have to. 不用了 Instead you can simply create a runnable: 相反,您可以简单地创建一个可运行的:

Runnable alarmRunnable = new Runnable() {
        @Override
        public void run() {
            for(int i=600; i>0;i-- ){
                try{
                    Thread.sleep(1000);
                    String cc = ""+i;
                    lab1.setText(cc);
                }
                catch(InterruptedException e){
                    System.out.println("Chyba!");
                }
            }
        }
    };

    Thread t = new Thread(alarmRunnable);
    t.start();

The arrow notation (otherwise known as lambda) I think is cleaner though. 我认为箭头符号(也称为lambda)更干净。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM