线程等待并唤醒

时间:2012-07-16 09:48:28

标签: java multithreading locking wait wakeup

我正在尝试使用多线程来模拟一个简单的恒温器。恒温器应该升高温度,以达到用户要求的值,该值是下面代码中的“最大”值。我有两个线程,一个负责提高温度,另一个负责降低温度。但减少的条件是它应该只在气体关闭时运行。 但我在实施这个概念时遇到了问题。当下面的代码运行时,第二个线程不断抛出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());
            }
        }
    }
};
`

2 个答案:

答案 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);