在空的无限循环中进行选项检查 vs. 在执行某操作的无限循环中进行操作

huangapple go评论72阅读模式
英文:

Checking options in empty Infinite loop vs dosomething infite loop

问题

public static void main(String... s) {
    StartUp obj = new StartUp();
      
    while(true) {
        //System.out.println("Option - " + option);
        if(option == 1) {
            option = 0;
            obj.setVisible(false);
            obj.dispose();
            new Test();
            break;
        } else if(option == 2) {
            option = 0;
            obj.setVisible(false);
            obj.dispose();
            new PWorld.Splash().load();
            break;
        }
    }
}

I need to put System.out.println("Option - " + option); inside the while loop for it to work otherwise the program freezes after running StartUp obj = new StartUp();

option is a static int inside the StartUp class and is changed by an ActionListener.
The value in option is changed by the ActionListener, but the while loop doesn't seem to work.

But if I put System.out.println("Option - " + option); inside the while loop, it works. WHY!

I'm using this while loop because new PWorld.Splash().load(); has Thread.sleep(), and as answered in this post, a new JFrame will not be drawn if called from an ActionListener (in the UI Thread) which has Thread.sleep().

Thank you


<details>
<summary>英文:</summary>

    public static void main(String... s) {
            StartUp obj = new StartUp();
              
    		while(true) {
    			//System.out.println(&quot;Option - &quot; + option);
    			if(option == 1) {
    				option = 0;
    				obj.setVisible(false);
    				obj.dispose();
    				new Test();
    				break;
    			}else if(option == 2) {
    				option = 0;
    				obj.setVisible(false);
    				obj.dispose();
    				new PWorld.Splash().load();
    				break;
    			}
    		}
        }
		

I need to put `System.out.println(&quot;Option - &quot; + option);` inside the while loop for it to work otherwise the programe freezes after running StartUp obj = new StartUp();

`option` is a static int inside StartUp class and is changed by a Actionlistener
the value in `option` is changed by ActionListener but while loop doesn&#39;t seems to work.

But if I put `System.out.println(&quot;Option - &quot; + option);` inside while loop, it works. **WHY!**

I&#39;m using this while loop because `new PWorld.Splash().load();` has `Thread.sleep()`, and [as in this answere][1] new JFrame will not be drawn if called from ActionListener(in UI Thread)which has `Thread`.

*Thank you* 


  [1]: https://stackoverflow.com/questions/36363060/jframe-not-loading-when-calling-from-other-class

</details>


# 答案1
**得分**: 1

你的问题是:

* 你正在调用一个“紧密”的循环,这会占用CPU并阻塞其他代码的运行。`System.out.println(...)` 语句会添加导致循环变慢的代码,从紧密循环中释放CPU,允许其他线程运行,这就是你问题的根源。
* 话虽如此,你的编码方法也不好,因为你正在使用 `while (true)` 循环来代替对事件的响应,这不是编写 Swing GUI 应该采用的方式。
* 你指出原因是循环中的一段代码调用了 `Thread.sleep`,如果在 Swing 事件线程上调用此代码(例如在 ActionListener 中),将会阻塞事件线程,导致GUI冻结 - 这是完全正确的。
* 但你的解决方案是错误的。正确的解决方案不是在主方法的 `while (true)` 循环中调用它,而是要么从后台线程中调用 `Thread.sleep`,比如在 [SwingWorker](https://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html) 的 `doInBackground()` 方法中(链接是教程),或者更好的办法是使用 [Swing Timer](https://docs.oracle.com/javase/tutorial/uiswing/misc/timer.html)(同样,链接是教程)来替代 `Thread.sleep`。这将允许你的代码暂停一些操作,而不阻塞 Swing 事件线程。
* 另一种选择是,如果你需要显示一个对话框(子窗口),可以使用模态 JDialog 来显示一个窗口,同时阻止与主GUI窗口的交互,直到对话框窗口不再可见。

要获得更详细和全面的解决方案,请考虑创建并发布你的 [最小可复现示例](https://stackoverflow.com/help/minimal-reproducible-example) 程序与你的问题。

以下是我的最小可复现示例:

```java
import java.awt.Dialog.ModalityType;
import java.awt.Dimension;
import java.awt.Color;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Window;
import javax.swing.*;

public class MinReproExample {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            Startup startup = new Startup();
            startup.showStartUp();

            Option option = startup.getOption();
            if (option == Option.TEST) {
                JOptionPane.showMessageDialog(null, "Test selected", "Selection", JOptionPane.DEFAULT_OPTION);
            } else if (option == Option.PWORLD) {
                PWorld pworld = new PWorld();
                pworld.showSplash();
            }
        });
    }
}

// ...(其他部分被省略)

这是一个最小可复现示例,展示了如何使用 Swing 创建一个界面。

英文:

Your issues are:

  • You're calling a "tight" loop, one that hogs the CPU and blocks other code from running. The System.out.println(...) statement adds code that slows this loop, releasing the CPU from the jaws of the tight loop, allowing other threads to run, and this is the genesis of your question.
  • Having said that, again, your coding approach is not good, in that you're using a while (true) loop in place of responding to an event, which is how Swing GUI's should be coded.
  • You state that the reason for this is that one bit of code in the while loop calls a Thread.sleep and that this code, if called on the Swing event thread, such as within an ActionListener, will block the event thread, freezing your GUI -- all true.
  • But your solution is wrong. The correct solution is not to call this in a while (true) loop in the main method, but rather to either call the Thread.sleep from a background thread, such as within the doInBackground() method of a SwingWorker (link is to tutorial), or better still, to use a Swing Timer (again, link is to tutorial) in place of the Thread.sleep. This will allow your code to pause some code without blocking the Swing event thread.
  • Another option, if you need to show a dialog (sub) window is to use a modal JDialog to show a window while blocking interaction with the main GUI window, until the dialog window is no longer visible.

For a more detailed and comprehensive solution, again, please consider creating and posting your Minimal, Reproducible Example program with your question.

For example, here is my Minimal, Reproducible Example:

import java.awt.Dialog.ModalityType;
import java.awt.Dimension;
import java.awt.Color;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Window;
import javax.swing.*;

public class MinReproExample {
	public static void main(String[] args) {
		SwingUtilities.invokeLater(() -&gt; {
			Startup startup = new Startup();
			startup.showStartUp();

			Option option = startup.getOption();
			if (option == Option.TEST) {
				JOptionPane.showMessageDialog(null, &quot;Test selected&quot;, &quot;Selection&quot;, JOptionPane.DEFAULT_OPTION);
			} else if (option == Option.PWORLD) {
				PWorld pworld = new PWorld();
				pworld.showSplash();
			}	
		});
	}
}

<!-- -->

class Startup {
	private JDialog startupDialog;
	private Option option = null;
	
	public Startup() {
		ButtonGroup buttonGroup = new ButtonGroup();
		JPanel optionsPanel = new JPanel(new GridLayout(1, 0, 10, 10));
		optionsPanel.setBorder(BorderFactory.createTitledBorder(&quot;Options&quot;));
		for (final Option op : Option.values()) {
			JRadioButton rBtn = new JRadioButton(op.getText());
			rBtn.setActionCommand(op.getText());
			optionsPanel.add(rBtn);
			buttonGroup.add(rBtn);
			rBtn.addActionListener(e -&gt; {
				option = op;
				Window window = SwingUtilities.getWindowAncestor(optionsPanel);
				window.dispose();
			});
		}
		
		startupDialog = new JDialog(null, &quot;Select Option&quot;, ModalityType.APPLICATION_MODAL);
		startupDialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
		startupDialog.add(optionsPanel);
		startupDialog.pack();
		startupDialog.setLocationRelativeTo(null);
	}
	
	public void showStartUp() {
		if (startupDialog != null) {
			startupDialog.setVisible(true);
		}
	}
	
	public Option getOption() {
		return option;
	}
}

<!-- -->

class PWorld {
	private static final Color ROBINS_EGG_BLUE = new Color(0, 204, 204);
	private JDialog pworldSplashDialog;
	private JFrame mainPWorldFrame;

	public PWorld() {
		JLabel splashLabel = new JLabel(&quot;Splash Window&quot;, SwingConstants.CENTER);
		JPanel splashPanel = new JPanel(new GridBagLayout());
		splashPanel.add(splashLabel);
		splashPanel.setBackground(Color.PINK);
		splashPanel.setPreferredSize(new Dimension(300, 250));

		pworldSplashDialog = new JDialog(null, &quot;Splash&quot;, ModalityType.MODELESS);
		pworldSplashDialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
		pworldSplashDialog.add(splashPanel);
		pworldSplashDialog.pack();
		pworldSplashDialog.setLocationRelativeTo(null);

		JLabel mainLabel = new JLabel(&quot;Main GUI Window&quot;, SwingConstants.CENTER);
		JPanel mainPanel = new JPanel(new GridBagLayout());
		mainPanel.add(mainLabel);
		mainPanel.setBackground(ROBINS_EGG_BLUE);
		mainPanel.setPreferredSize(new Dimension(500, 350));

		mainPWorldFrame = new JFrame(&quot;Main PWorld GUI&quot;);
		mainPWorldFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		mainPWorldFrame.add(mainPanel);
		mainPWorldFrame.pack();
		mainPWorldFrame.setLocationRelativeTo(null);

	}

	public void showSplash() {
		int timerDelay = 2000; // two second delay
		Timer timer = new Timer(timerDelay, e -&gt; {
			if (pworldSplashDialog != null &amp;&amp; pworldSplashDialog.isVisible()) {
				pworldSplashDialog.dispose();
				showMainPWorldFrame();
			}
		});
		timer.setRepeats(false);
		timer.start();

		pworldSplashDialog.setVisible(true);
	}

	private void showMainPWorldFrame() {
		mainPWorldFrame.setVisible(true);
	}
}

<!-- -->

// options to choose from
enum Option {
	TEST(&quot;Test&quot;), PWORLD(&quot;PWorld&quot;);
	private String text;

	private Option(String text) {
		this.text = text;
	}

	public String getText() {
		return text;
	}
}

答案2

得分: 0

这个循环如果在初始输入时选项不是1或2,预期会做什么?你只是在无意义地消耗 CPU 周期,等待其他线程去做一些事情。

添加打印语句会注入一些非 CPU 消耗的延迟,这样的话也许会让设置 'option' 的线程开始运行。

(顺便说一下,如果你期望对 'option' 进行的更改对其他线程可见,很可能需要将其声明为 volatile。)

这不是一个好的设计。我无法从上下文中足够了解你应该做什么,但某种形式的良好通知机制是必要的。但这应该能回答你的“为什么”的问题。

英文:

What is this loop expected to do if option is not 1 or 2 on initial entry? You're just burning CPU cycles for no reason, waiting for some other thread to do something.

Adding the print statement injects a bit of non-CPU-burning delay, in which case maybe the thread that's going to set 'option' gets to run.

(FWIW, 'option' likely needs to be declared volatile if you're expecting changes to be made visible to other threads).

This is not a good design. I can't tell enough about the context to tell you what you should do, but some sort of decent notification mechanism is needed. But this should answer your question of 'WHY?'.

huangapple
  • 本文由 发表于 2020年8月30日 03:09:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/63650820.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定