我正在尝试编写一个方法,要求设备输入,然后接受响应,所有这些都是原子操作。
这是我的代码(query
方法确实应该关注的内容):
public class DeviceConnection implements Runnable{
//For query
static int test = 0;
//For writeline
static PrintWriter out = null; //(initialized in constructor)
//Only important for readline
static String[] systemMessage=new String[10];
static int messageIn=0;
static int messageOut=0;
static boolean connected = false;
static boolean endConnect = true;
static PrintStream logWriter; //(initialized in constructor)
static String serverName="";//(initialized in constructor)
static int socketNum;//(initialized in constructor)
/** REALLY ONLY NEED TO LOOK AT THIS METHOD
* Atomic operation to ask for data from device
* and get a response.
* @param line - query to be passed to device
* @return response from device
*/
public synchronized String query(String line){
int temp = test;
System.err.print("foo" + test);
System.err.print(this);
String response;
writeLine(line);
response = readLine();
System.err.println("bar" + test + " should be " + temp);
test = temp+1;
return response;
}
/**
* Writes a query to the device.
*<p>
* Does <b>not</b> get or handle any response
* @param line - query to be passed to device
*/
public synchronized void writeLine(String line) {
out.println(line + "\n");
}
/**
* Reads a response from device.
*<p>
* Should never be used outside of <code>query</code>
* as this could create a race condition if another
* thread is writing to the device at the same time.
* @return
*/
private synchronized String readLine() {
String response;
long start, stop;
if (messageIn != messageOut) { // new message exists
response = systemMessage[messageOut];
messageOut = (messageOut + 1) % 10;
} else {
start = System.currentTimeMillis();
try {
if (connected) { // if connected wait for heatbeats
//wait(15000);
wait();
start = System.currentTimeMillis();
} else { // if not connected ignore heartbeats
wait();
start = System.currentTimeMillis();
}
} catch (InterruptedException e) { return "Interrupted"; }
stop = System.currentTimeMillis();
if (stop - start < 12000) { // heart beats running at 5 s
if (messageIn != messageOut) { // new message exists
response = systemMessage[messageOut];
messageOut = (messageOut + 1) % 10;
} else {
return null;
}
} else { // heart beats lost
response = "Heart beats lost";
logWriter.println(response);
if (connected) { // connection lost on client side
logWriter.println("Connection to " + serverName +
" lost on client side");
sleep(60000);
connect(serverName,socketNum);
}
}
}
return response;
}
}
通常query
方法运行正常,但有时我会得到如下输出:
foo59lib.DeviceConnection@7bd0bf6d(other System.out stuff printed here)
foo59lib.DeviceConnection@7bd0bf6dbar59 should be 59
bar60 should be 59
这怎么可能? Aren在对象上锁定/同步的方法是什么?该对象显然与打印显示的相同,但不知何故,两个query
方法同时执行。
答案 0 :(得分:4)
从readLine
调用的query
方法调用wait,释放锁定,这将使另一个线程同时调用query
。
您应该始终使用条件变量在循环内调用wait
,使用if来判断是否等待是有缺陷的。一旦你的线程重新获得锁定,它需要检查当前的状态。
Object#wait
的文档中解释了wait
释放锁的问题:
当前线程必须拥有此对象的监视器。线程 释放此监视器的所有权并等待另一个线程 通知在此对象的监视器上等待的线程也会唤醒 通过调用notify方法或notifyAll方法。该 线程然后等待,直到它可以重新获得监视器的所有权和 恢复执行。
在一个参数版本中,中断和虚假唤醒是 可能,这个方法应该总是在循环中使用:
synchronized (obj) { while (<condition does not hold>) obj.wait(); ... // Perform action appropriate to condition }