0

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. each time I press the Up button in order to increase the desired temperature the GUI freezes. How can I solve this problem?

It's like a thread is holding the lock and won't release it but how can I notice which one?

`private volatile boolean isBoilerOn = false;
protected int Max, Current;
protected boolean isDone, isGasOn, isPumpOn;
private temperatureUp tempUp;
private temperatureDown tempDown;

public void setBoilerSatus(boolean status) {
    this.isBoilerOn = status;            
}

public boolean getBoilerStatus() {
    return this.isBoilerOn;           
}

private synchronized void setGasBoilerStatus() {
if(Max>Current)
    setBoilerSatus(true);
else
    setBoilerSatus(false);

notifyAll();
}

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
{
while (!getBoilerStatus()) 
    wait();



if(Max>Current)
{
    Thread.sleep(4000);
    Current ++;
}

setGasBoilerStatus();
}

private synchronized void decreaseTemeture() throws InterruptedException
{
while(getBoilerStatus())    wait();

Thread.sleep(4000);
if(Current == 0 )
    return;

Current --;

setGasBoilerStatus();
}

private void systemInitial()
{
isGasOn = false;
Max = Current = 0;
}

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());
        }
    }
}
};`
Afflatus
  • 933
  • 1
  • 12
  • 39
  • `setGasBoilerStatus` is a synchronized method. Your freeze occurs when you call that method from the Swing thread and it can never acquire the lock. – Marko Topolnik Jul 25 '12 at 11:57
  • @SoboLAN sorry, but one of the users asked me to put the executable code here. – Afflatus Jul 25 '12 at 12:27
  • @MarkoTopolnik I did notice the freezing is due to use of `invokeLater` but I don't know what should I put in its place. I need to call the synchronized method after my jButton got released. – Afflatus Jul 25 '12 at 12:29
  • Actually, `invokeLater` has nothing to do with it and is in fact redundant. You are calling `invokeLater` from the Swing thread, which is very rarely called for. – Marko Topolnik Jul 25 '12 at 12:31
  • @MarkoTopolnik if I dismiss the `invokeLater` calling `setGasBoilerStatus` will cause the freezing. would you please tell me how I should call `setGasBoilerStatus`? – Afflatus Jul 25 '12 at 13:08
  • Exactly---no difference whether you use it or not, and when you fix your locking, it will go away for both as well. Another advice: use VisualVM or a simple thread dump to find out which thread is holding your lock. – Marko Topolnik Jul 25 '12 at 13:09
  • ok, I will google VisualVM to find out what's causing this headache. :] – Afflatus Jul 25 '12 at 13:16

2 Answers2

1
  • don't use invokeLater() for starting / manage / stop the Background task setGasBoilerStatus(); ???, you have issue with Concurency in Swing

  • SwingWorker isn't designated to running long task repeatelly, use Runnable#Thread instead

  • in the case that your task is simple and you want to run this taks only once time (from JButtons click) then to use SwingWorker

  • basically is possible invoke Event Dispatch Thread from your code, required to use invokeLater() but only for update of Swing JComponents, for example code line txtLog.setText(w + "\n" + txtLog.getText()); should be wrapped into invokeLater(),

  • since setText() is declared as Thread Safe I saw there tons questions where that isn't true

  • in the case that txtLog.setText(w + "\n" + txtLog.getText()); is about JTextArea to use append() instead of setText(whatever + getText())

  • for better help sooner post an SSCCE demonstrated issue, mabye there is/are another issue

mKorbel
  • 109,525
  • 20
  • 134
  • 319
  • ok, I used this but this time after pressing Up nothing happens! `Thread t = new Thread(new Runnable() { @Override public void run() { setGasBoilerStatus(); } }); t.start();` – Afflatus Jul 25 '12 at 12:38
0

This is happening because your are not using invokeLater correctly. Take a look here: Java AWT Threads

Community
  • 1
  • 1
davidbuzatto
  • 9,207
  • 1
  • 43
  • 50
  • when the Up button is pushed the threads should be notified. if I called the setGasBoilerStatus method without using invokeLater it tells me that I can't notify threads in unsynchronized region. according to the articles you introduced I shouldn't use SwingWorker or invokeLater . but what should I do then? – Afflatus Jul 25 '12 at 12:00
  • Is this code inside in just one JFrame? This frame depends on another frames? Can you put your complete code or something that can be tested? – davidbuzatto Jul 25 '12 at 12:02
  • @Elham Just because it told you that you can't use `notify` without acquiring the lock first doesn't mean that the solution is to try and acquire the lock. Your code is permanently holding that lock from some other place. You must learn the basics of Java's locking in order to make progress with your code. – Marko Topolnik Jul 25 '12 at 12:08
  • @MarkoTopolnik when the system initialize the temperature is 0 and unless the user increase the desired temperature the thread responsible for increasing it is on wait so I assumed on pressing the Up button I should call the method which is incharge of change the status of GasBoiler and notifying the threads. is this logic wrong? – Afflatus Jul 25 '12 at 12:15
  • It is not possible to analyze your code down to the last detail, but let me tell you what to try: put a `println` just before calling `setGasBoilerStatus` in the "up" button handler, and then another `println` at the top of that method. See what gets printed. – Marko Topolnik Jul 25 '12 at 12:19