我正在尝试使用多线程来模拟一个简单的恒温器。恒温器应该升高温度,以达到用户要求的值,该值是下面代码中的“最大”值。我有两个线程,一个负责提高温度,另一个负责降低温度。但减少的条件是它应该只在气体关闭时运行。 但我在实施这个概念时遇到了问题。当下面的代码运行时,第二个线程不断抛出null的异常!
<pre><code>`private void formWindowActivated(java.awt.event.WindowEvent evt) {
systemInitial();
Thread temperatureUp = new Thread()
{
@Override
public void run()
{
while(true)
{
Max = Integer.parseInt(lblDesiredTemp.getText());
Current = Integer.parseInt(lblCurrentTemp.getText());
try
{
if(Max>Current)
{
lblGasStatus.setText("On");
lblTemperatureSensor.setText("increasing");
increaseTemeture();
}
else
{
lblGasStatus.setText("Off");
if(Current != 0)
lblTemperatureSensor.setText("decreasing");
else
lblTemperatureSensor.setText("----");
}
}
catch(Exception ex)
{
txtLog.setText(ex.getMessage() + "\n" + txtLog.getText() );
}
}
}
};
Thread systemUpdater = new Thread()
{
@Override
public void run()
{
while(true)
{
try
{
notifyGasBoiler(this);
if(Current>0)
decreaseTemeture();
}
catch(Exception ex)
{
txtLog.setText(ex.getMessage() + "\n" + txtLog.getText() );
}
}
}
};
temperatureUp.start();
systemUpdater.start();
}
private synchronized void notifyGasBoiler(Thread gasOff) throws InterruptedException
{
try
{
if("On".equals(lblGasStatus.getText()))
{
gasOff.wait();
txtLog.setText(txtLog.getText() + "\n" + gasOff.getName() + " waits.");
}
else
notifyAll();
}
catch (Exception ex)
{
txtLog.setText(ex.getMessage() + "\n" + txtLog.getText() );
}
}`
我在这里错过了什么?
更新I:
这是我通过运行系统并请求2温度得到的日志:
温度升至1 温度升至2℃ 空值 空值 空值 ....
更新II:
我使用printStackTrace进行异常处理,它让我知道了:
java.lang.IllegalMonitorStateException
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:485)
at sol.smarthome.GUI.notifyGasBoiler(GUI.java:300)
at sol.smarthome.GUI.access$900(GUI.java:14)
at sol.smarthome.GUI$5.run(GUI.java:276)
更新III
<pre><code>`private void btnUpActionPerformed(java.awt.event.ActionEvent evt) {
if(Max<=8)
{
Max++;
String strI = String.valueOf(Max);
lblDesiredTemp.setText(strI);
setGasBoilerStatus();
}
}
private void btnDownActionPerformed(java.awt.event.ActionEvent evt) {
if(Max>0)
{
Max--;
String strI = String.valueOf(Max);
lblDesiredTemp.setText(strI);
setGasBoilerStatus();
}
}
private void formWindowActivated(java.awt.event.WindowEvent evt) {
systemInitial();
tempUp = new temperatureUp();
tempDown = new temperatureDown();
tempUp.start();
tempDown.start();
}
private synchronized void increaseTemeture() throws InterruptedException
{
synchronized (monitor) {
if (!getBoilerStatus())
{
tempUp.wait();
//return;
}
else
{
Max = Integer.parseInt(lblDesiredTemp.getText());
Current = Integer.parseInt(lblCurrentTemp.getText());
if(Max>Current)
{
lblGasStatus.setText("On");
lblTemperatureSensor.setText("increasing");
Thread.sleep(4000);
Current ++;
lblPumpStatus.setText("On");
lblCurrentTemp.setText(String.valueOf(Current));
txtLog.setText("Temperature increased to " + Current + "\n"+ txtLog.getText());
if(Current>8)
lblDanger.setVisible(true);
}
setGasBoilerStatus();
if(!isGasOn)
{
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
//Logger.getLogger(GUI.class.getName()).log(Level.SEVERE, null, ex);
}
lblGasStatus.setText("Off");
if(Current != 0)
lblTemperatureSensor.setText("decreasing");
else
lblTemperatureSensor.setText("----");
}
}
}
}
private synchronized void decreaseTemeture() throws InterruptedException
{
synchronized (monitor) {
if(getBoilerStatus())
{
tempDown.wait();
//return;
}
else
{
Thread.sleep(4000);
if(Current == 0 )
return;
Current --;
lblCurrentTemp.setText(String.valueOf(Current));
lblDanger.setVisible(false);
txtLog.setText("Temperature decreased to " + Current + "\n"+ txtLog.getText());
if(Current<1)
lblPumpStatus.setText("Off");
else
lblPumpStatus.setText("On");
setGasBoilerStatus();
}
}
}
private void systemInitial()
{
lblDanger.setVisible(false);
isPilotOn.setSelected(true);
lblGasStatus.setText("Off");
lblPumpStatus.setText("Off");
isDone = true;
isGasOn = false;
Max = Current = 0;
}
// indicates if the boiler is on (true) or off (false)
// set as volatile to stop caching
private volatile boolean isBoilerOn = false;
protected int Max, Current;
protected boolean isDone, isGasOn, isPumpOn;
private temperatureUp tempUp;
private temperatureDown tempDown;
// Used to synchronize thread access to internal state (Current and
// isBoilerOn member variables. The monitor is private in order
// for this class to encapsulate its synchronization policy.
private final Object monitor = new Object();
// update the bolier's status to on (true) or off (false)
public void setBoilerSatus(boolean status) {
synchronized (monitor) {
// block threads until boiler is switched on
this.isBoilerOn = status;
// (see below), this is the place to notify them...
notifyAll();
}
}
// returns true if the boiler is on, false otherwise
public synchronized boolean getBoilerStatus() {
synchronized (monitor) {
return this.isBoilerOn;
}
}
private void setGasBoilerStatus() {
synchronized (monitor) {
if(Max>Current)
setBoilerSatus(true);
else
setBoilerSatus(false);
}
}
class temperatureUp extends Thread
{
@Override
public void run()
{
while(true)
{
try
{
increaseTemeture();
}
catch(Exception ex)
{
StringWriter w = new StringWriter();
ex.printStackTrace(new PrintWriter(w));
//txtLog.setText(w + "\n" + txtLog.getText());
}
}
}
};
class temperatureDown extends Thread
{
@Override
public void run()
{
while(true)
{
try
{
decreaseTemeture();
}
catch(Exception ex)
{
StringWriter w = new StringWriter();
ex.printStackTrace(new PrintWriter(w));
//txtLog.setText(w + "\n" + txtLog.getText());
}
}
}
};
`
答案 0 :(得分:1)
尝试创建一个类恒温器,它封装了恒温器的状态和行为。诀窍是使用适当的同步来维护程序的不变量。您可以在下面找到基于您的要求说明的样本实施的恒温器类,用于说明目的。请注意如何使用同步来保留不变量。任何影响当前温度的方法(例如up(int)和down(int))可以由不同的线程同时调用,而不会由于同步而发生竞赛事件或相关危险。
同样,这仅用于说明目的:
public final class Thermostat {
// constant for maximum allowable temperature
public static final int MAX_TEMP = 100;
// the thermostat's current temperature
private int temp = 0;
// indicates if the boiler is on (true) or off (false)
private boolean boilerStatus = false;
public Thermostat() {
}
// Used to synchronize thread access to internal state (temp and
// boilerStatus member variables. The monitor is private in order
// for this class to encapsulate its synchronization policy.
private final Object monitor = new Object();
// update the bolier's status to on (true) or off (false)
public void setBoilerOn(boolean status) {
synchronized (monitor) {
this.boilerStatus = status;
// if you block threads until boiler is switched on
// (see below), this is the place to notify them...
}
}
// returns true if the boiler is on, false otherwise
public boolean isBoilerOn() {
synchronized (monitor) {
return this.boilerStatus;
}
}
// increase the thermostat's temperature by the specified units
// provided that the boiler has been set on
public void up(int units) {
synchronized (monitor) {
// don't increase the temperature if the boiler
// is not turned on...
if (!isBoilerOn()) {
// you could alternatively wait here if your
// thread needs to block...
return;
}
// increase the temperature
if ((temp + units) <= MAX_TEMP) {
temp += units;
} else {
// TODO : handle incorect user input here...
}
}
}
// decrease the thermostat's temperature by the specified units
// (negative values allowed)
public void down(int units) {
synchronized (monitor) {
temp -= units;
}
}
}
答案 1 :(得分:0)
尝试使用Full class name (dot) this
例如:
notifyGasBoiler(MyTempClass.this);