私は単純な問題だと思っていましたが、まだ良い解決策を見つけていません: Swing インターフェイスパネルのボタンを押すことで、スレッドで行われているアクティビティを一時停止および再開できるようにしたいと考えています。
具体的には、1 つのスレッドを使用してオーディオ フレームをリアルタイムで取り込みたいと考えています。これらのフレームで魔法の処理を実行する 2 番目のスレッド。結果をシリアル化し、ソケットを介して別の場所に送信する 3 番目のスレッド。キッカーは、使用する魔法のブランドによっては、2 番目のスレッドでの処理が実際のデータ収集よりもフレームあたりの実行に時間がかかり、しばらくするとデータが蓄積される可能性があることです。
非常に大雑把なプロトタイプの回避策として、音声収集プロセスのオンとオフを切り替えるボタンとステータス バー (後で実装する予定) を備えた GUI を追加して、ユーザーが音声収集プロセスがどれだけいっぱいになっているかを監視できるようにしようと考えました。バッファー (リンクされたブロッキング キュー) がたまたま存在していました。
これは予想以上に大変です。問題をおもちゃのバージョンに落とし込みました: 50 個の整数を格納できるリンクされたブロッキング キュー、GUI、2 つのスレッド (異なる速度でキューに追加およびキューから削除)、およびブール値をラップする Token オブジェクト。それはこのように見えます、そしてそれはちょっとうまくいきます:
テスト.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())
ループ内でのポーリングが犠牲になります。Locks と Conditions を使用しようとしましたが、失敗し、常に IllegalMonitorStateExceptions が発生しました。
そして、私はこの同様の質問を見て、なんとかそれを機能させることができましたが、Java 5 と Java 6 で明らかに大きく異なり、非常に落胆しているように見える yield() メソッドを使用することを犠牲にしました。
だから私の質問: 私がやろうとしていることを行うための正しい、またははるかに良い方法はありますか? ポーリングなしで信頼できる方法でこれを実現する方法があるはずです。
更新:アプリケーションで何らかの方法でオーディオ キャプチャ ループを制御する問題を回避できるかどうかはわかりません。ボタンを押すのが人間であろうと、他の要因に基づいて決定を下す内部ロジックであろうと、私たちは本当に、その忌まわしいものをシャットダウンしてコマンドで復活させることができる必要があります.