我已经制作了三个课程:Reader
,Writer
和Main
课程。 Reader
类使用线程来侦听Python解释器标准输出的输出。
Reader
类:
package main;
import java.io.BufferedReader;
import java.io.IOException;
public class Reader implements Runnable {
private volatile BufferedReader br;
private volatile String output;
private Thread thread;
private Boolean stop;
public Reader(BufferedReader br) {
this.br = br;
this.output = "";
this.thread = new Thread(this);
this.stop = false;
}
public void run() {
while(!stop) {
this.read();
}
}
private synchronized void read() {
try {
if(br.ready()) {
this.output = br.readLine();
System.out.println(this.output);
notify();
wait();
}
}
catch(Exception error) {
error.printStackTrace();
}
}
public synchronized String getOutput() {
try {
wait();
}
catch(Exception error) {
error.printStackTrace();
}
notify();
return this.output;
}
public void startListening() {
this.thread.start();
}
public void close() throws IOException {
this.stop = true;
this.br.close();
}
}
这里是Writer
班级:
package main;
import java.io.BufferedWriter;
import java.io.IOException;
public class Writer {
private BufferedWriter bw;
public Writer(BufferedWriter bw) {
this.bw = bw;
}
public void write(String line) {
try {
this.bw.write(line);
this.bw.newLine();
this.bw.flush();
}
catch(Exception error) {
error.printStackTrace();
}
}
public void close() throws IOException {
this.bw.close();
}
}
最后,Main
类看起来如下所示。
package main;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
public class Main {
public static void main(String[] args) throws IOException,
InterruptedException {
ProcessBuilder processBuilder = new ProcessBuilder("/usr/bin/script",
"-qfc", "/usr/bin/python",
"/dev/null");
Process process = processBuilder.start();
InputStream inputStream = process.getInputStream();
InputStreamReader isr = new InputStreamReader(inputStream);
BufferedReader br = new BufferedReader(isr);
OutputStream outputStream = process.getOutputStream();
OutputStreamWriter osw = new OutputStreamWriter(outputStream);
BufferedWriter bw = new BufferedWriter(osw);
Writer writer = new Writer(bw);
Reader reader = new Reader(br);
reader.startListening();
writer.write("2+2");
System.out.print(reader.getOutput());
}
}
我得到的唯一结果是两行输出,第一行在第二行开始时重复。
Python 2.6.6 (r266:84292, Jan 22 2014, 09:42:36).
Python 2.6.6 (r266:84292, Jan 22 2014, 09:42:36). [GCC 4.4.7] on linux2
尽管剩余更多输出,readLine
方法看起来仍然没有继续阅读。为什么?我希望我的程序能够给出结果,如下几行所示。提前感谢您的帮助。
Python 2.6.6 (r266:84292, Jan 22 2014, 09:42:36)
[GCC 4.4.7 20120313 (Red Hat 4.4.7-4)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> 2+2
4
>>>
答案 0 :(得分:1)
只有一种正确的方法可以使用wait()
和notify()
。
首先,必须有明确的条件。它可以是布尔变量,也可以是布尔函数,但它必须是可以测试的东西。
其次,必须存在与条件关联的锁定,并且任何可以更改条件的代码必须仅在锁定锁定时执行此操作。
最后,wait()
的线程必须在循环中执行。
看起来像这样:
// lock is private so we don't have to worry about outside code messing with our synchronization.
private final Object lock = new Object();
// condition is private so we don't have to worry that outside code could break the contract
// (i.e., change the condition without locking the lock.)
private boolean condition = false;
// called by thread A
void waitForIt() {
synchronized(lock) {
while (! condition) {
// At this point condition is false. We don't have to worry about a "lost
// notification" because this thread holds the lock, and will continue to
// hold it until it is in the wait() call, ready to be notified.
lock.wait();
// condition might *not* be true at this point because thread C might have
// set it false in the interval between when thread B called notify(), and
// when this thread finally re-acquired the lock and returned from wait().
}
// condition *is* guaranteed to be true here because we've tested it, and
// we have the exclusive lock.
doSomethingThatRequiresConditionToBeTrue();
}
// condition is no longer guaranteed true after leaving the synchronized block.
}
// called by thread B
void makeItHappen() {
synchronized(lock) {
if (! condition) {
doSomethingThatMakesConditionTrue();
condition = true;
lock.notify();
}
}
}
// called by thread C
void reset() {
synchronized(lock) {
doSomethingThatMakesConditionFalse();
condition = false;
}
}
通过这种设计,通知不会丢失"如果线程B在线程A调用makeItHappen()
之前调用waitForIt().
waitForIt()
函数不会盲目地等待:它只在条件显式为假时等待。
答案 1 :(得分:1)
如下面的答案所示,您使用了synchronized
,notify
,
wait
和volatile
以不符合您认为的方式行事的System.out.println
和Reader startListening() entry
Writer write() entry. line: 2+2
Writer write() exit.
Reader getOutput() entry
Reader getOutput() wait...
Reader run() entry
Reader read() entry
Reader read() exit
<snip - lots and lots of empty read() entry and exits...>
Reader read() br is ready, readLine...
Python 2.6.6 (r266:84292, Nov 22 2013, 12:16:22)
Reader read() notify...
Reader read() wait...
Reader getOutput() notify...
Reader getOutput() is returning: Python 2.6.6 (r266:84292, Nov 22 2013, 12:16:22)
Python 2.6.6 (r266:84292, Nov 22 2013, 12:16:22) Reader read() exit
Reader read() entry
Reader read() br is ready, readLine...
[GCC 4.4.7 20120313 (Red Hat 4.4.7-4)] on linux2
Reader read() notify...
Reader read() wait...
做。
而不是为您提供一套全新的代码 使用日志语句向您展示您的代码当前正在做什么, 然后提供一组最小的更改,以使您当前的代码工作。
此外,查看您的CPython交互式shell输出,因为它们都是 CPython 2.6.6和Red Hat发行版我猜测你正在使用CentOS。 因此,我对以下内容进行了测试:
所以,让我们开始吧。我撒了很多Reader getOutput()
个电话
到处;每个函数入口,函数出口和决策一个
(例如,如果陈述)。我不会在这里发布完整的输出
是一个摘要版本:
wait()
使用适当的日志记录库可以更容易地阅读 线程名称或ID,但因为我们可以猜出涉及哪些线程 让我们解开这个:
Reader run() entry
。此实例方法是同步的,因此
主线程在调用Reader read()
时阻塞,释放Reader
实例监视器锁定,并等待某人通知它。br.readLine()
。notify()
次来电。synchronized
,打印出一行
输出,然后调用wait()
。有趣!我们有什么锁
我们正在通知?实例锁被抓住了
Reader read() notify
关键字,即Reader实例!我们知道的
主线程是当前notify()
在Reader上的唯一线程
object作为监视器,因此该调用会导致主线程被唤醒。notify()
点,问问自己:什么是主要的
线程干嘛?我们在Reader实例上wait()
,所以肯定是
主线程正在运行?没有! notify()
的JavaDoc非常好
清除:&#34;唤醒的线程将无法继续直到
当前线程放弃对该对象的锁定。&#34;。所以在这
指向读者线程告诉主线程#34;醒来,但等待
直到我放弃了Reader实例监视器&#34;。getOutput()
,因此放弃Reader
实例监视器对象并进入睡眠状态。这导致主要
线程恢复执行。notify()
。采用与此前相同的逻辑
唤醒Reader线程,但Reader线程可能只会恢复
一旦主线程放弃了Reader实例监视器。wait()
的调用已完成。这回来了
第一行&#34; Python 2.6.6&#34;再次。这解释了为什么这一行
打印两次。wait()
。这个电话无关紧要,
因为主线程不再expect
在这个锁上。package main;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
public class Main {
public static boolean isProcessFinished(Process process) {
try {
process.exitValue();
} catch (IllegalThreadStateException e) {
return false;
}
return true;
}
public static void printAllOutput(Reader reader) {
String line;
while ((line = reader.getOutput()) != null) {
System.out.println(line);
}
}
public static void main(String[] args) throws IOException, InterruptedException {
ProcessBuilder processBuilder = new ProcessBuilder(
"/usr/bin/script", "-qfc", "/usr/bin/python", "/dev/null");
Process process = processBuilder.start();
InputStream inputStream = process.getInputStream();
InputStreamReader isr = new InputStreamReader(inputStream);
BufferedReader br = new BufferedReader(isr);
OutputStream outputStream = process.getOutputStream();
OutputStreamWriter osw = new OutputStreamWriter(outputStream);
BufferedWriter bw = new BufferedWriter(osw);
try (
Writer writer = new Writer(bw);
Reader reader = new Reader(br);
) {
reader.startListening();
writer.write("2+2");
writer.write("exit()");
while(!isProcessFinished(process)) {
// We don't block here. Maybe we're doing something
// complicated at the same time. Maybe we're a web
// server. Maybe...
printAllOutput(reader);
// Without this the java process hits 100% CPU.
// This is because we're furiously checking if the
// process has exited and looking for output. Java's
// process handling API isn't so hot; consider using
// another library?
Thread.sleep(100);
}
// Yes, the process has finished, but there may still be output
// to be read from the BufferedReader. Let's grab any dog-ends.
printAllOutput(reader);
}
}
}
。由于主线程不再存在
周围通知它唤醒程序永远阻止。需要一段时间才能调试!我们学到了什么?的多线程 编程真的很难。我们只有两个线程!在这 通过奇怪的使用来实现复杂性的情况 同步方法并等待并通知同步方法。
我们知道其他的事情。这个计划没有合理的理由 打印出程序的所有输出行。这是因为 线程尚未在此程序中有效使用。
在我继续之前,我应该强调写一个期待的
Java中的程序并非易事。我强烈建议你重复使用
执行此操作的现有库,直接package main;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
public class Reader implements Runnable, AutoCloseable {
private BufferedReader br;
private Thread thread;
private Queue<String> output = new ConcurrentLinkedQueue<>();
private AtomicBoolean stop = new AtomicBoolean();
public Reader(BufferedReader br) {
this.br = br;
this.thread = new Thread(this);
}
public void run() {
System.out.println("Reader run() entry");
while(!stop.get()) {
try {
// Why not just avoid the ready() call and block
// on readLine()? We want the Main thread to be able
// to stop the Reader thread at any time. Hence the
// Reader thread needs to periodically check to see
// if it has been requested to stop, and moreover
// the Reader thread cannot block.
while (br.ready()) {
output.add(br.readLine());
}
} catch (IOException e) {
e.printStackTrace();
stop.set(true);
}
}
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("Reader run() exit");
}
public String getOutput() {
return output.poll();
}
public void startListening() {
System.out.println("Reader startListening() entry");
this.thread.start();
}
public void close() throws IOException {
// Note that the close() method is called by the Main thread
// and merely sets a flag. This flag will eventually be read
// by the Reader thread which will then close the
// BufferedReader.
this.stop.set(true);
}
}
或其他库
有人已经写过的Java克隆。
但是,让我们说我们想以最直接的方式按原样修复程序 可能。我认为这对你来说是一个很好的学习机会 在Java中使用线程练习。作为一个起点问问自己: 你想要什么?我们希望能够发送&#34; 2 + 2&#34;运行Python proccess并且不强制主线程阻塞等待输出 从过程中。这表明使用了一个独特的线程 责任是从流中读取输出。那么主线是 可以随时从读者线程获得一行输出 to,或弄清楚当前没有可用的输出。
我们如何知道流程何时完成?当然读者会 不知道。它只是从一个流中读取,从它的角度来看 流是无穷无尽的,如果空了,读者会阻止阅读它!至 保持简单,让我们说这是主要的责任 知道什么时候过程;什么时候Main类会读取所有的 剩余输出,打印,然后退出。
从我们编写此描述的方式来看,读者已经清楚了 应该尽可能频繁地阅读。读者 应该不知道主线程存在。相反,读者 线程应该把它的结果放到一个安全的地方。然后是主线程 可以经常/偶尔或同样地从这个安全的地方读取 主线程想要的很少。这看似简单直接 解决方案是将此输出存储到并发队列中 某种。 Reader线程将添加到头部和主线程 会从尾巴弹出!使用并发队列委托讨厌 将线程同步到线程安全集合的业务,以及 整齐地将读者线程与知道谁正在从中读取。
Main.java:
package main;
import java.io.BufferedWriter;
import java.io.IOException;
public class Writer implements AutoCloseable {
private BufferedWriter bw;
public Writer(BufferedWriter bw) {
this.bw = bw;
}
public void write(String line) {
System.out.println("Writer write() entry. line: " + line);
try {
this.bw.write(line);
this.bw.newLine();
this.bw.flush();
}
catch(Exception error) {
error.printStackTrace();
}
System.out.println("Writer write() exit.");
}
public void close() throws IOException {
this.bw.close();
}
}
Reader.java:
Reader startListening() entry
Writer write() entry. line: 2+2
Writer write() exit.
Writer write() entry. line: exit()
Writer write() exit.
Reader run() entry
Python 2.6.6 (r266:84292, Nov 22 2013, 12:16:22)
[GCC 4.4.7 20120313 (Red Hat 4.4.7-4)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> 4
>>>
Reader run() exit
Writer.java(除了实现AutoCloseable之外没有改变)
expect
输出:
expect
作为读者的练习 - 你将如何真正地与之互动 Python进程?那么你需要一种向它发送输入的方法 等待&#34;&gt;&gt;&gt;&#34;从它表明它已准备好接收 更多输入。如果执行命令,例如,该怎么办? &#34;打印&#39;&gt;&gt;&gt;&#39;,那 包含提示?那么你肯定会判断是否 解释器已准备好输入,按ENTER然后查看是否 输出&#34;&gt;&gt;&gt;&#34;。但是,如果它正在运行命令而你呢? 按ENTER,肯定会扭曲输出?
慢慢但肯定会重新实施{{1}}。有趣的运动,我 在C中完成了{{1}}的限制版本,但这并非易事。