【发布时间】:2012-06-17 18:22:14
【问题描述】:
我有一个我认为很简单的问题,但我还没有找到一个好的解决方案:我希望能够通过点击 Swing 界面上的按钮来暂停和取消暂停线程中发生的活动面板。
具体来说,我想用一个线程实时接收音频帧;第二个线程对这些帧执行魔术处理;第三个线程将结果序列化并通过套接字发送到其他地方。更重要的是,根据我们使用的魔法品牌,第二个线程中的每帧处理可能需要比实际数据收集更长的时间来执行,并且数据可能会在一段时间后堆积起来。
作为非常粗略的原型解决方法,我们认为我们应该添加一个带有按钮的 GUI,以打开和关闭音频收集过程和一个状态栏(稍后实现),以便用户可以密切关注缓冲区(链接阻塞队列)的填充程度。
这比我预期的要难。我将问题简化为一个玩具版本:一个可以存储 50 个整数的链接阻塞队列、一个 GUI、两个线程(以不同的速率添加到队列中和从队列中删除)和一个包裹在布尔值周围的 Token 对象。它看起来像这样,它有点工作:
Test.java
public class Test {
public static void main(String[] args) throws IOException {
Token t1 = new Token();
Token t2 = new Token();
LinkedBlockingQueue<Integer> lbq = new LinkedBlockingQueue<Integer>(50);
startFill sf = new startFill(t1, lbq);
startEmpty se = new startEmpty(t2, lbq);
TestUI testUI = new TestUI(t1, t2, lbq);
testUI.setVisible(true);
sf.start();
se.start();
}
}
TestUI.java
public class TestUI extends JFrame implements ActionListener {
private JToggleButton fillStatus, emptyStatus;
public boolean filling, emptying;
public Token t1, t2;
public LinkedBlockingQueue<Integer> lbq;
public TestUI(Token t1, Token t2, LinkedBlockingQueue<Integer> lbq) {
this.t1 = t1;
this.t2 = t2;
this.lbq = lbq;
initUI();
}
public synchronized void initUI() {
JPanel panel = new JPanel();
panel.setLayout(null);
filling = false;
fillStatus = new JToggleButton("Not Filling");
fillStatus.setBounds(20, 20, 150, 25);
fillStatus.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
if (filling == false) {
fillStatus.setText("Filling");
} else {
fillStatus.setText("Not Filling");
}
filling = !filling;
t1.flip();
System.out.println("fill button press");
}
});
// Similar code for actionListener on Empty button, omitted
panel.add(fillStatus);
panel.add(emptyStatus);
add(panel);
setTitle("Test interface");
setSize(420, 300);
setLocationByPlatform(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
public void actionPerformed(ActionEvent e) {
}
}
startFill.java
public class startFill extends Thread {
public Token token;
public LinkedBlockingQueue<Integer> lbq;
public startFill(Token token, LinkedBlockingQueue<Integer> lbq) {
this.token = token;
this.lbq = lbq;
}
public void run() {
int count = 0;
while (true) {
while (!token.running()) {
try {
sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
while (token.running()) {
try {
lbq.put(count);
System.out.println("queue size = " + lbq.size());
count++;
sleep(100);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
}
}
}
还有一个 startEmpty.java 的工作方式大致相同,还有一个 Token.java 是一个布尔状态变量的包装器,为了仁慈的简洁而省略。
这样可行,但代价是在while (!token.running()) 循环中进行轮询。
我尝试使用锁和条件,但失败了,总是得到 IllegalMonitorStateExceptions。
我查看了这个similar question 并设法让它工作,但代价是使用了在 Java 5 和 Java 6 之间明显不同的 yield() 方法,并且似乎非常不鼓励。
所以我的问题是:是否有一些正确或明显更好的方法来做我想做的事情?似乎应该有一种方法可以在没有轮询且可靠的情况下实现这一点方法。
更新:我不确定能否解决以某种方式为应用程序控制音频捕获循环的问题。无论是人类按下按钮,还是基于其他因素做出决策的内部逻辑,我们确实需要能够关闭该死的东西并根据命令使其恢复活力。
【问题讨论】:
标签: java multithreading swing concurrency