我一直在制作倒计时节目,我想出了这个。
package main;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;
public class Gatoo extends JFrame implements ActionListener {
private int sec, min, secTot, since = 999;
private long lastTime;
private JTextField mm = new JTextField(2), ss = new JTextField(2);
private JLabel minLab = new JLabel("Minutes:"), secLab = new JLabel(
"Seconds:");
private JButton start = new JButton("Start");
private Clip done;
private boolean started = false;
private static final long serialVersionUID = 4277921337939922028L;
public static void main(String[] args) {
Gatoo cake = new Gatoo("Title");
cake.pack();
cake.setSize(800, 600);
cake.setLocationRelativeTo(null);
cake.setDefaultCloseOperation(3);
cake.setVisible(true);
cake.run();
}
public Gatoo(String s) {
super(s);
setLayout(new FlowLayout());
start.addActionListener(this);
add(minLab);
add(mm);
add(secLab);
add(ss);
add(start);
}
@Override
public void actionPerformed(ActionEvent e) {
started = true;
}
public void play(File file) throws MalformedURLException,
UnsupportedAudioFileException, IOException,
LineUnavailableException {
AudioInputStream ais = AudioSystem.getAudioInputStream(new File(
"lib/done.wav"));
DataLine.Info info = new DataLine.Info(Clip.class, ais.getFormat());
done = (Clip) AudioSystem.getLine(info);
done.open(ais);
done.start();
}
public void run() {
while (true) {
System.out.print("");// needed?
if (started) {
try {
min = Integer.parseInt(mm.getText());
sec = Integer.parseInt(ss.getText());
secTot = (min * 60) + sec;
lastTime = System.currentTimeMillis();
while (secTot > 0) {
since = (int) (System.currentTimeMillis() - lastTime);
if (since > 998) {
lastTime = System.currentTimeMillis();
secTot--;
}
}
play(new File("done.wav"));
} catch (NumberFormatException exception) {
System.out.println("Minutes and seconds must be numbers.");
return;
} catch (Exception exception) {
exception.printStackTrace();
}
started = false;
}
}
}
}
在最后的while循环中,如果没有print / println语句,倒计时代码就不会执行。怎么会?该程序与print语句完美配合。
答案 0 :(得分:12)
首先,您的程序线程不安全,因为boolean started
是共享变量,但它既不是volatile
也不是{同步块。
现在,偶然地,PrintStream#print
是一种同步方法,在任何实际架构中,使用内存屏障 CPU指令实现进入和退出同步块,这会导致完全同步线程本地状态和主存储器。
因此,纯事故,添加print
调用允许一个线程(EDT)设置started
标志被另一个线程(主线程)看到)。
答案 1 :(得分:4)
Swing应用程序的设计很差。
while(true)
循环。详细了解Concurency in Swing。 run()
(例如Listeners
)的帮助下呼叫事件,而不是标记(ActionListener
此处)。Timer
。更改您的started
方法,如下一步:
run()
public void run() {
min = Integer.parseInt(mm.getText());
sec = Integer.parseInt(ss.getText());
secTot = (min * 60) + sec;
Timer timer = new Timer(1000*secTot, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
play(new File("done.wav"));
} catch (Exception e1) {
e1.printStackTrace();
}
}
});
timer.start();
}
方法:
actionPerformed()
并删除@Override
public void actionPerformed(ActionEvent e) {
run();
}
方法中的cake.run()
。
答案 2 :(得分:2)
看,我做了一个SSCCE再现这种行为。 这是一个非常好的问题。
public class ThreadRacing implements Runnable
{
public boolean started = false;
public static void main(String[] args)
{
new ThreadRacing().test();
}
public void test()
{
new Thread(this).start();
try
{
Thread.sleep(1000);
} catch (Exception e)
{
}
started = true;
System.out.println("I did my job");
}
@Override
public void run()
{
while (true)
{
//System.out.print("");
if (started)
{
System.out.println("I started!!");
}
}
}
}
这印刷:“我完成了我的工作”。而已。添加volatile
关键字实际上可以解决问题。
对我来说,看起来第二个帖子没有收到有关started
更新的通知,因为他太忙了。
答案 3 :(得分:-1)
我猜测你的繁忙等待循环正在严重占用CPU,它无法做任何事情。 print语句引起了足够的线程上下文切换,以便能够完成其他工作。
编辑:好的,我做了一点测试。我能够在HotSpot Server VM上重现OP的问题。使用Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
没有修复它,所以它不是一个饥饿问题。将变量设置为volatile
为@MartinCourteau,@ MarkoTopolnik建议,确实修复了它。那讲得通。我最初无法在HotSpot客户端虚拟机上重现该问题;显然,它的优化太弱,无法缓存started
变量。
(尽管如此,如果Java音频线程的线程优先级低于正常,并且它是单CPU系统,那么饥饿就是一个看似合理的假设。)