peteykun
peteykun

Reputation: 716

Calling 'wait' and 'notify' on this

I have gone through pretty much every question on java multithreading here on SO, but I do not quite understand if my understanding is correct.

I wish to understand if the sequence of operations plays out as I have described below.

import javax.swing.*;
import java.awt.FlowLayout;
import java.awt.event.*;

class Runner implements Runnable{
    long count = 0;
    JLabel label;
    boolean paused = false;

    public Runner(JLabel label){
        this.label = label;
    }

    public void run(){
        while(true){
            try {
                if(paused){
                    synchronized(this){
                        wait();
                    }
                }
            } catch(InterruptedException e) {}
                count++;
                label.setText(Long.toString(count));
                if(Thread.interrupted())
                    return;
        }
    }

    synchronized void pause_counting(){
        paused = true;
    }

    synchronized void start_counting(){
        paused = false;
        notifyAll();
    }
}

public class ThreadRace extends JApplet{
    boolean running = true;
    Thread thread;

    @Override
    public void init(){
        final JPanel panel = new JPanel();
        panel.setLayout(new FlowLayout());

        JLabel threadProgress = new JLabel("0");
        final Runner runner = new Runner(threadProgress);
        thread = new Thread(runner);
        thread.start();

        panel.add(threadProgress);

        JButton toggle = new JButton("Stop");
        toggle.addActionListener(new ActionListener() {
            @Override
            synchronized public void actionPerformed(ActionEvent e) {
                System.out.println("Running: " + running);
                if(running){
                    runner.pause_counting();
                } else {
                    runner.start_counting();    
                }
                running = !running;
            }
        });
        panel.add(toggle);

        add(panel);
    }

    public void stop(){
        thread.interrupt();
    }
}

Is this how it works?

  1. When I call the method pause_counting(), I am setting paused to false.
  2. Inside of run(), I am taking hold of the lock to the object, runner inside of the synchronized(this) { ... } block.
  3. When I make a call to this.wait(), I am releasing hold of the same lock which I just took possession of and go into the WAITING state.
  4. At this point, any other method is free to do anything with runner (!)
  5. I make a call to start_counting() eventually.
  6. This makes the thread go back into the RUNNING state and continues from after the wait() statement. While it is still inside the synchronized(this) block, it gets hold of the lock
  7. However, once it leaves the block, it releases the lock and execution continues

I am pretty sure there are a few flaws in my understanding.

Help? :(

Upvotes: 2

Views: 132

Answers (1)

Gray
Gray

Reputation: 116938

Is this how it works?

Yes. All of your statements are true with some tweaks (see below).

However, there is are bugs in your program. Because the thread is testing for paused while not inside of a synchronized block, you need to make sure that paused is volatile to provide memory synchronization.

volatile boolean paused = false

This is probably also necessary with the JLabel array as well. Any time you have a threads that are changing shared variables, there needs to be memory synchronization.


When I call the method pause_counting(), I am setting paused to false.

Yes. Updating paused here is ok because it is in a synchronized method.

Inside of run(), I am taking hold of the lock to an object, runner[i] inside of the synchronized(this) { ... } block.

Only if paused is true. Since you are testing for paused outside of the synchronized block, paused needs to be volatile to ensure memory synchronization between threads.

When I make a call to this.wait(), I am releasing hold of the same lock which I just took possession of and go into the WAITING state.

Right.

At this point, any other method is free to do anything with this runner[i] (!)

Well it was free to do stuff before but now that the lock is released it can call synchronized methods without blocking.

I make a call to start_counting() eventually.

Right. This calls notifyAll().

This makes the thread go back into the RUNNING state and continues from after the wait() statement. While it is still inside the synchronized(this) block, it gets hold of the lock

Well first it makes the thread go into the BLOCKED state until the caller of start_counting() leaves that method and releases the lock.

However, once it leaves the block, it releases the lock and execution continues

Right.

Upvotes: 2

Related Questions