简体   繁体   中英

Thread Wait and Wake up

I'm trying to simulate a simple thermostat, using multi-threading. The thermostat should increase the temperature in order to reach the value that the user requested for which is "Max" value in the code below. I have two thread, one in charge of increasing the temperature and the other for decreasing it. but the condition for decreasing is it should be only running when the gas is off. but I have a problem with implementing this concept. as the code below runs, the second thread keeps throwing exception of 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() );
    }
}`

what am I missing here?

UPDATE I:

this is the log I'm getting by running the system and request the 2 temperature:

Temperature increased to 1 Temperature increased to 2 null null null ....

UPDATE II:

I used printStackTrace for exception and it got me this:

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)


Update 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());
            }
        }
    }
};
`

Try creating a class Thermostat that encapsulate's your thermostat's state and behaviors. The trick is to use proper synchronization in order to maintain your program's invariants. Below you may find a sample implementation of a Thermostat class based on your requirements description, for illustration purposes . Notice how synchronization is used in order to preserve your invariants. Any method (such as up(int) and down(int) which both affect the current temperature) may be invoked concurrently by different threads, without race events or related hazards due to synchronization.

Again, this is for illustration purposes only:

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;          
        }
    }
}

Try using the Full class name (dot) this

eg:

notifyGasBoiler(MyTempClass.this);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM