线程睡眠在actionPerformed方法中

时间:2022-10-25 20:59:02

First of all I want to say I'm aware this aproach is wrong so I'm asking this question because of pure curiousity. Lets say I have a swing application like this:

首先,我想说我知道这种方法是错误的所以我问这个问题是因为纯粹的好奇心。可以说我有一个像这样的swing应用程序:

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

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

public class ThreadSleeping {
    JFrame frame = new JFrame();
    JPanel panel = new JPanel();
    JButton button = new JButton("Load");
    JLabel label = new JLabel();

    public ThreadSleeping() {
        panel.add(button);

        button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent arg0) {
                label.setIcon(new ImageIcon(
                        "C:/Users/Public/Pictures/Sample Pictures/Tulips.jpg"));
                System.out.println("Tulips painted");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                label.setIcon(new ImageIcon(
                        "C:/Users/Public/Pictures/Sample Pictures/Koala.jpg"));
                System.out.println("Koala painted");

            }
        });

        frame.add(panel, BorderLayout.NORTH);
        frame.add(label, BorderLayout.CENTER);
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.setSize(1024, 768);
        // frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new ThreadSleeping();
            }
        });
    }
}

Basically when I click a Load button I expect that Tulips.jpg image displays then GUI freezes for a 2 seconds and after that I expect that Koala.jpg image displays. But what happens is that: I click on button, GUI freezes for a 2 seconds and Koala.jpg displays. No Tulips.jpg before that. But thing that confuses me is when I put those System.out.println("Tulips painted"); and System.out.println("Koala painted");. So when I click on button it prints "Tulips painted" and after 2 seconds "Koala painted". Can someone tell me whats going on here? Regards.

基本上当我点击加载按钮时,我希望显示Tulips.jpg图像,然后GUI冻结2秒钟,之后我希望显示Koala.jpg图像。但是会发生的是:我点击按钮,GUI冻结了2秒钟,并显示Koala.jpg。之前没有Tulips.jpg。但令我困惑的是当我把那些System.out.println(“郁金香画”);和System.out.println(“考拉画”);.因此,当我点击按钮时,它会打印“郁金香画”,并在2秒后“考拉画”。谁能告诉我这里发生了什么?问候。

2 个解决方案

#1


3  

  1. works in this case, because you programatically freeze ouf Swing GUI, but there is/aren't another update(s), ot another JComponent(s)

    在这种情况下工作,因为你以编程方式冻结了Swou GUI,但是有/不是另一个更新,而是另一个JComponent(s)

  2. doens't works in the case that there are a few another updated to the Swing GUI, Thread.sleep(int) freeze Event Dispatch Thread,

    在没有更新到Swing GUI的情况下,dons't工作,Thread.sleep(int)冻结事件调度线程,

  3. by default all updates to the JComponents XxxModels never will be visible on the JComponents view

    默认情况下,对JComponents XxxModel的所有更新都不会在JComponents视图中可见

  4. example until sleep ended you'll lost all updated to the GUI

    例如,直到睡眠结束,你将失去所有更新到GUI

#2


2  

The point I intended to make in my comment:

我打算在评论中提出的观点:

if you sleep the edt, the resulting mis-behaviour is basically unpredictable.

如果你睡觉了edt,那么产生的错误行为基本上是不可预测的。

Unpredictable in the sense that you can't know what will happen or not. All we can do, is guess ...

从某种意义上讲是不可预知的,你无法知道会发生什么。我们所能做的就是猜...

The technical reason is that most ui updates don't happen immediately but are scheduled: we can't really know what's waiting in the line behind us. Remember: it's only one lane, and when in the actionPerformed, it's we that are sitting in it.

技术上的原因是,大多数ui更新不是立即发生的,而是安排的:我们无法真正知道在我们后面排队的是什么。请记住:它只是一条通道,当它在actionPerformed中时,我们坐在它里面。

For educational reasons, the code below is the original code with a couple of lines to un/comment to demonstrate different scenarios.

出于教育原因,下面的代码是原始代码,有几行代表/评论以演示不同的场景。

  • [0] resetting the icon before/after sleeping: as you already noticed, the first doesn't show even though the property is taken. Technical reason: visual update happens via label.repaint() which is scheduled on the EDT for latter processing (as its api doc states)
  • [0]在睡觉之前/之后重置图标:正如您已经注意到的那样,即使拍摄了该属性,第一个也不显示。技术原因:视觉更新通过label.repaint()发生,该标签在EDT上安排进行后续处理(如其api doc所述)
  • [1] skimming the api doc, we notice another method: paintImmediately(...) which is documented to do exactly what it's name says and allowed - as we are on the EDT - is allowed to be called. Looks like success, the yellow icon shows up.
  • [1]浏览api文档,我们注意到另一种方法:paintImmediately(...)被记录为完全按照它的名字所说和允许 - 正如我们在EDT上 - 被允许被调用。看起来成功,黄色图标显示出来。
  • [2] but wait: being in the center of a Borderline, the label fills that area anyway, independent of whether or not it has an icon. Let's try to put it into a region that requires a re-layout, as f.i. into the south. Back to square [0], yellow icon not showing.
  • [2]但是等待:在边界线的中心,标签无论如何填充该区域,无论它是否有图标。让我们尝试将其放入需要重新布局的区域,如f.i.进入南方。回到正方形[0],黄色图标不显示。
  • [3] looking into the source of setIcon(..) reveals that layout is ... scheduled again. We learned in square [1] that we can force thingies to happen immediately, in case of layout that would be the pair invalidate() / validate(). Bingo, yellow icon even when in south.
  • [3]查看setIcon(..)的来源显示布局是......再次安排。我们在square [1]中了解到,如果布局成为无效的()/ validate(),我们可以立即强制发生。宾果,黄色图标,即使在南方。
  • [4] nasty subclass which schedules the icon property setting (note: while contrived here there is nothing in its contract that hinders subclasses to do it!). As the property isn't even set, yellow isn't showing, back to square [0]
  • [4]令人讨厌的子类,它安排了图标属性设置(注意:虽然这里设计的合同中没有任何内容妨碍子类做到这一点!)。由于属性没有设置,黄色没有显示,回到正方形[0]

At the end of the day (but before going to sleep the EDT :-), there is simply no way to reliably predict the visual outcome during the sleep. And visuals are only the tip of the ice...

在一天结束时(但在睡觉之前,EDT :-),根本无法可靠地预测睡眠期间的视觉结果。视觉效果只是冰的一角......

/**
 * Unpredictable behaviour when sleeping the EDT.
 * http://*.com/q/15600203/203657
 * 
 * [0] run as-is: property set but yellow not showing
 * [1] uncomment paintImmediately: yellow showing in center
 * [2] add label to south: yellow not showing
 * [3] force immediate in-/validation: yellow showing in south
 * [4] subclass label with invoked property setting: 
 *       property not set, yellow not showing
 * 
 */
public class ThreadSleeping {
    JFrame frame = new JFrame();
    JPanel panel = new JPanel();
    JButton button = new JButton("Load");
    JLabel label = new JLabel() {
// [4] subclass implemented to schedule the property setting         
//        @Override
//        public void setIcon(final Icon icon) {
//            SwingUtilities.invokeLater(new Runnable() {
//                public void run() {
//                    superSetIcon(icon);
//                    
//                }
//            });
//        }
//        
//        protected void superSetIcon(Icon icon) {
//            super.setIcon(icon);
//        }
//        
    };

    public ThreadSleeping() {
        panel.add(button);

        button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent arg0) {
                Icon firstIcon = new FixedIcon(Color.YELLOW, 100, 100);
                Icon secondIcon = new FixedIcon(Color.RED, 500, 500);
                label.setIcon(firstIcon);
                // check if the property is already set
                System.out.println(label.getIcon());
                // following lines try to force the output before going to sleep
                // [3] paintImmediately + force immediate re-layout 
                //  label.invalidate();
                //  label.getParent().validate();
                // {1] paintImmediately (fine for center, no effect for south)
                // ((JComponent) label.getParent()).paintImmediately(0, 0, 5000, 5000);
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                label.setIcon(secondIcon);

            }
        });

        frame.add(panel, BorderLayout.NORTH);
        // un/comment one of the following, placing the
        // label either in CENTER (= sized to fill)
        frame.add(label, BorderLayout.CENTER);
        // [2] or in SOUTH (= sized to pref)
        // frame.add(label, BorderLayout.SOUTH);
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.setSize(1024, 768);
        frame.setVisible(true);
    }

    /**
     * Simple fixed size Icon filling its area.
     */
    public static class FixedIcon implements Icon {

        private Color background;
        private int width;
        private int height;

        public FixedIcon(Color background, int width, int height) {
            this.background = background;
            this.width = width;
            this.height = height;
        }
        @Override
        public void paintIcon(Component c, Graphics g, int x, int y) {
            g.setColor(background);
            g.fillRect(0, 0, width, height);
        }

        @Override
        public int getIconWidth() {
            return width;
        }

        @Override
        public int getIconHeight() {
            return height;
        }
        @Override
        public String toString() {
            return "[" + background + " w/h " + width + "/" + height + "]";
        }


    }
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new ThreadSleeping();
            }
        });
    }
}

#1


3  

  1. works in this case, because you programatically freeze ouf Swing GUI, but there is/aren't another update(s), ot another JComponent(s)

    在这种情况下工作,因为你以编程方式冻结了Swou GUI,但是有/不是另一个更新,而是另一个JComponent(s)

  2. doens't works in the case that there are a few another updated to the Swing GUI, Thread.sleep(int) freeze Event Dispatch Thread,

    在没有更新到Swing GUI的情况下,dons't工作,Thread.sleep(int)冻结事件调度线程,

  3. by default all updates to the JComponents XxxModels never will be visible on the JComponents view

    默认情况下,对JComponents XxxModel的所有更新都不会在JComponents视图中可见

  4. example until sleep ended you'll lost all updated to the GUI

    例如,直到睡眠结束,你将失去所有更新到GUI

#2


2  

The point I intended to make in my comment:

我打算在评论中提出的观点:

if you sleep the edt, the resulting mis-behaviour is basically unpredictable.

如果你睡觉了edt,那么产生的错误行为基本上是不可预测的。

Unpredictable in the sense that you can't know what will happen or not. All we can do, is guess ...

从某种意义上讲是不可预知的,你无法知道会发生什么。我们所能做的就是猜...

The technical reason is that most ui updates don't happen immediately but are scheduled: we can't really know what's waiting in the line behind us. Remember: it's only one lane, and when in the actionPerformed, it's we that are sitting in it.

技术上的原因是,大多数ui更新不是立即发生的,而是安排的:我们无法真正知道在我们后面排队的是什么。请记住:它只是一条通道,当它在actionPerformed中时,我们坐在它里面。

For educational reasons, the code below is the original code with a couple of lines to un/comment to demonstrate different scenarios.

出于教育原因,下面的代码是原始代码,有几行代表/评论以演示不同的场景。

  • [0] resetting the icon before/after sleeping: as you already noticed, the first doesn't show even though the property is taken. Technical reason: visual update happens via label.repaint() which is scheduled on the EDT for latter processing (as its api doc states)
  • [0]在睡觉之前/之后重置图标:正如您已经注意到的那样,即使拍摄了该属性,第一个也不显示。技术原因:视觉更新通过label.repaint()发生,该标签在EDT上安排进行后续处理(如其api doc所述)
  • [1] skimming the api doc, we notice another method: paintImmediately(...) which is documented to do exactly what it's name says and allowed - as we are on the EDT - is allowed to be called. Looks like success, the yellow icon shows up.
  • [1]浏览api文档,我们注意到另一种方法:paintImmediately(...)被记录为完全按照它的名字所说和允许 - 正如我们在EDT上 - 被允许被调用。看起来成功,黄色图标显示出来。
  • [2] but wait: being in the center of a Borderline, the label fills that area anyway, independent of whether or not it has an icon. Let's try to put it into a region that requires a re-layout, as f.i. into the south. Back to square [0], yellow icon not showing.
  • [2]但是等待:在边界线的中心,标签无论如何填充该区域,无论它是否有图标。让我们尝试将其放入需要重新布局的区域,如f.i.进入南方。回到正方形[0],黄色图标不显示。
  • [3] looking into the source of setIcon(..) reveals that layout is ... scheduled again. We learned in square [1] that we can force thingies to happen immediately, in case of layout that would be the pair invalidate() / validate(). Bingo, yellow icon even when in south.
  • [3]查看setIcon(..)的来源显示布局是......再次安排。我们在square [1]中了解到,如果布局成为无效的()/ validate(),我们可以立即强制发生。宾果,黄色图标,即使在南方。
  • [4] nasty subclass which schedules the icon property setting (note: while contrived here there is nothing in its contract that hinders subclasses to do it!). As the property isn't even set, yellow isn't showing, back to square [0]
  • [4]令人讨厌的子类,它安排了图标属性设置(注意:虽然这里设计的合同中没有任何内容妨碍子类做到这一点!)。由于属性没有设置,黄色没有显示,回到正方形[0]

At the end of the day (but before going to sleep the EDT :-), there is simply no way to reliably predict the visual outcome during the sleep. And visuals are only the tip of the ice...

在一天结束时(但在睡觉之前,EDT :-),根本无法可靠地预测睡眠期间的视觉结果。视觉效果只是冰的一角......

/**
 * Unpredictable behaviour when sleeping the EDT.
 * http://*.com/q/15600203/203657
 * 
 * [0] run as-is: property set but yellow not showing
 * [1] uncomment paintImmediately: yellow showing in center
 * [2] add label to south: yellow not showing
 * [3] force immediate in-/validation: yellow showing in south
 * [4] subclass label with invoked property setting: 
 *       property not set, yellow not showing
 * 
 */
public class ThreadSleeping {
    JFrame frame = new JFrame();
    JPanel panel = new JPanel();
    JButton button = new JButton("Load");
    JLabel label = new JLabel() {
// [4] subclass implemented to schedule the property setting         
//        @Override
//        public void setIcon(final Icon icon) {
//            SwingUtilities.invokeLater(new Runnable() {
//                public void run() {
//                    superSetIcon(icon);
//                    
//                }
//            });
//        }
//        
//        protected void superSetIcon(Icon icon) {
//            super.setIcon(icon);
//        }
//        
    };

    public ThreadSleeping() {
        panel.add(button);

        button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent arg0) {
                Icon firstIcon = new FixedIcon(Color.YELLOW, 100, 100);
                Icon secondIcon = new FixedIcon(Color.RED, 500, 500);
                label.setIcon(firstIcon);
                // check if the property is already set
                System.out.println(label.getIcon());
                // following lines try to force the output before going to sleep
                // [3] paintImmediately + force immediate re-layout 
                //  label.invalidate();
                //  label.getParent().validate();
                // {1] paintImmediately (fine for center, no effect for south)
                // ((JComponent) label.getParent()).paintImmediately(0, 0, 5000, 5000);
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                label.setIcon(secondIcon);

            }
        });

        frame.add(panel, BorderLayout.NORTH);
        // un/comment one of the following, placing the
        // label either in CENTER (= sized to fill)
        frame.add(label, BorderLayout.CENTER);
        // [2] or in SOUTH (= sized to pref)
        // frame.add(label, BorderLayout.SOUTH);
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.setSize(1024, 768);
        frame.setVisible(true);
    }

    /**
     * Simple fixed size Icon filling its area.
     */
    public static class FixedIcon implements Icon {

        private Color background;
        private int width;
        private int height;

        public FixedIcon(Color background, int width, int height) {
            this.background = background;
            this.width = width;
            this.height = height;
        }
        @Override
        public void paintIcon(Component c, Graphics g, int x, int y) {
            g.setColor(background);
            g.fillRect(0, 0, width, height);
        }

        @Override
        public int getIconWidth() {
            return width;
        }

        @Override
        public int getIconHeight() {
            return height;
        }
        @Override
        public String toString() {
            return "[" + background + " w/h " + width + "/" + height + "]";
        }


    }
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new ThreadSleeping();
            }
        });
    }
}