Swing中的线程和JLabel-无法正常工作

时间:2015-10-23 19:12:39

标签: java multithreading swing jbutton jlabel

请查看以下代码。

import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JFrame;
import javax.swing.JPanel;

class thread_jishu extends Thread{
    @Override
    public void run(){
        int p=-1;
        for(;;){
            //here continuously checking that whether
            //the value of label_thread.i is equals to p or not

            if(label_thread.i!=p){
                try{
                    Thread.sleep(1000);
                }catch(Exception e){}

                label_thread.lb.setText("after sleeping at -> "+label_thread.i);
                // here i want to set the JLabel
                // text after waiting 1sec only when
                // label_thread.i has been changed,
                // but not happening 
                p=label_thread.i;
            }
        }
    }
}

public class label_thread  implements java.awt.event.ActionListener{

    /**
     * @param evt
     */
    @Override
    public void actionPerformed(java.awt.event.ActionEvent evt){
        i+=1;
        lb.setText("Button clicked at -> "+i);
    }

    static int i=-1;
    static JLabel lb=new JLabel("hello here");
    static JFrame window=new JFrame("Jishu");
    static JFrame window2=new JFrame("Jishu");

    public static void main(String[] args) {
        // TODO code application logic here
        new thread_jishu().start();
        window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        window.setMinimumSize(new java.awt.Dimension(200,200));

        JButton bt=new JButton("Click here");
        bt.addActionListener(new label_thread());
        JPanel panel=new JPanel();
        panel.add(bt);
        window.add(panel);
        window2.add(lb);
        window.setVisible(true);
        window2.setVisible(true);
        window2.setMinimumSize(new java.awt.Dimension(200,200));
    }

}

JLabel的值不等于i时,我想重置第二个窗口中的p,这意味着在第一个窗口中点击了该按钮。

但点击按钮时JLabel的文字未被更改。

2 个答案:

答案 0 :(得分:0)

不应在AWT event thread之外修改Swing元素。如果您想在AWT事件线程中执行代码,可以使用SwingUtilities.invokeLater

类似的东西:

SwingUtilities.invokeLater(new Runnable() {
    @override
    public void run() {
        //Swing stuff here.
    }
});

会让你非常接近。

答案 1 :(得分:0)

存在许多潜在问题;

  • 过度使用/依赖static
  • 违反线程违规行为
  • 变量的线程内存访问
  • 一般的糟糕设计。

static没有帮助,从长远来看可能会导致各种问题。 static不是一个跨对象的通信机制,你应该学习其他没有它的交易技术

Swing是单线程框架,它不是线程安全的,这基本上意味着你不应该在事件调度线程的上下文中执行长时间运行的任务,你也不应该在上下文之外修改UI的状态。 EDT(比如从另一个线程设置标签的文本)。

Thread内部访问变量并且存在问题,因为Thread可以获得它自己的变量副本,这意味着对变量的读取和写入可以在线程,意味着他们可能看不到最新的价值。通常,您可以使用volatile关键字或使用原子API(例如AtomicInteger)解决此问题,但我认为通过更好的设计,您可以避免这些需求。

现在,您可以使用SwingWorker或Swing Timer,它们都提供了以节省方式控制EDT更新的解决方案,但您仍然使用线程。

你的程序受到基本的糟糕设计的影响,你将一堆属性暴露给不受控制的修改和访问,这使得很难解决类的责任(谁可以做什么以及何时做)。

所以,首先,我要定义一些控件

public interface Counter {
    public int getCount();
    public void setText(String text);
}

这个简单的界面提供对当前count值的访问,并为另一个类提供了设置实现文本的方法。这定义了"合同"。这意味着,无论我将此接口的实例传递给谁,他们只能执行这两项任务,并且由实现决定如何控制这些操作

接下来,我设置Thread ....

public class ThreadJishu implements Runnable {

    private Counter counter;
    private int initialCount;

    public ThreadJishu(Counter counter) {
        this.counter = counter;
    }

    @Override
    public void run() {
        this.initialCount = counter.getCount();
        while (true) {
            if (counter.getCount() !=  initialCount) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException ex) {
                }
                initialCount = counter.getCount();
                counter.setText("After sleep at -> " + initialCount);
            }
            Thread.yield();
        }
    }
}

所以,这与你正在做的不同,除了,我依靠Counter的实施来完成我需要做的工作。

最后,Counter的实施,CounterPane

public class CounterPane extends JPanel implements Counter {

    private int count = 0;
    private JLabel label;

    public CounterPane() {

        label = new JLabel("Hello");
        JButton btn = new JButton("Click here");

        setLayout(new GridBagLayout());
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.gridwidth = GridBagConstraints.REMAINDER;

        add(label, gbc);
        add(btn, gbc);

        btn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                count++;
                setText("Button click count = " + count);
            }
        });

        Thread t = new Thread(new ThreadJishu(this));
        t.start();
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(200, 200);
    }

    @Override
    public int getCount() {
        return count;
    }

    @Override
    public void setText(String text) {
        if (EventQueue.isDispatchThread()) {
            label.setText(text);
        } else {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    setText(text);
                }
            });
        }
    }

}

这为用户提供了界面,并定义了Counter的工作方式。在setText方法中,我们有一个安全的保护措施,可以确保对JLabel的所有修改都是在EDT的上下文中完成的,为简单起见,JButton' ActionListener实际上也使用setText方法。

可运行的例子......

Click Me

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class LabelThread {

    public static void main(String[] args) {
        new LabelThread();
    }

    public LabelThread() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new CounterPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public interface Counter {
        public int getCount();
        public void setText(String text);
    }

    public class CounterPane extends JPanel implements Counter {

        private int count = 0;
        private JLabel label;

        public CounterPane() {

            label = new JLabel("Hello");
            JButton btn = new JButton("Click here");

            setLayout(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridwidth = GridBagConstraints.REMAINDER;

            add(label, gbc);
            add(btn, gbc);

            btn.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    count++;
                    setText("Button click count = " + count);
                }
            });

            Thread t = new Thread(new ThreadJishu(this));
            t.start();
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200);
        }

        @Override
        public int getCount() {
            return count;
        }

        @Override
        public void setText(String text) {
            if (EventQueue.isDispatchThread()) {
                label.setText(text);
            } else {
                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        setText(text);
                    }
                });
            }
        }

    }

    public class ThreadJishu implements Runnable {

        private Counter counter;
        private int initialCount;

        public ThreadJishu(Counter counter) {
            this.counter = counter;
        }



        @Override
        public void run() {
            this.initialCount = counter.getCount();
            while (true) {
                if (counter.getCount() !=  initialCount) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException ex) {
                    }
                    initialCount = counter.getCount();
                    counter.setText("After sleep at -> " + initialCount);
                }
                Thread.yield();
            }
        }
    }

}

并发是一个复杂的主题,因为Swing API的需求(比如大多数GUI API)而变得更加复杂。

看看:

了解更多细节和常见问题的可能解决方案

Swing Timer示例......

使用Swing Timer的简单实现,不需要Thread

public class CounterPane extends JPanel implements Counter {

    private int count = 0;
    private JLabel label;

    private Timer timer;

    public CounterPane() {

        label = new JLabel("Hello");
        JButton btn = new JButton("Click here");

        setLayout(new GridBagLayout());
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.gridwidth = GridBagConstraints.REMAINDER;

        add(label, gbc);
        add(btn, gbc);

        btn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                count++;
                setText("Button click count = " + count);
                timer.restart();
            }
        });

        timer = new Timer(1000, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                setText("After sleep at -> " + getCount());
            }
        });
        timer.setRepeats(false);
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(200, 200);
    }

    @Override
    public int getCount() {
        return count;
    }

    @Override
    public void setText(String text) {
        label.setText(text);
    }

}