DNB5brims
DNB5brims

Reputation: 30568

How to stop and resume a Thread in a safe way in Java?

I am writing a heartbeat object that will keep sending request, but it may pause and resume....But I can stop it, but can't resume back....

Here is the HeartBeat Object:

public void startBeating() {
    heartBeatTask = new HeartBeatTask(monitorInterval);
    heartBeatTaskThread = new Thread(heartBeatTask);
}

public void stopBeating() {
    heartBeatTask.setKeepHeartBeat(false);
}

public void beating() {
    heartBeatTask.setKeepHeartBeat(true);
}

@Override
public void run() {
    this.startBeating();
    this.beating();
    heartBeatTaskThread.start();
}

And the heartBeatTask as follow:

public HeartBeatTask(long aHeartBeatInterval){
    this.heartBeatInterval = aHeartBeatInterval;
}

@Override
public void run() {
    while(isKeepHeartBeat()){
        System.out.println("beating");
        try {
            Thread.sleep(this.heartBeatInterval);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

Both object is a runnable object in java, what's going wrong that I can't resume the beating:

How I resume the beating:

    this.beating();

How I stop bearing:

    this.stopBeating();

Upvotes: 1

Views: 487

Answers (5)

Mavlarn
Mavlarn

Reputation: 3883

The problem of your code is that, in your stop method, you set "isKeepHeartBeat" as false, then your thread finished, but not paused.

So you need to use semaphore or wait() to let your thread to wait there, but not finished.

Upvotes: 0

Tom
Tom

Reputation: 44821

You could use a semaphore.

Here's how your example would play out:

public void startBeating() {
    semaphore = new Semaphore(1);
    heartBeatTask = new HeartBeatTask(monitorInterval, semaphore);
    heartBeatTaskThread = new Thread(heartBeatTask);
}

public void stopBeating() {
    try { 
        semaphore.aquire();
    } catch (InterruptedException e) {
    }
}

public void beating() {
    semaphore.release();
}

@Override
public void run() {
    this.startBeating();
    this.beating();
    heartBeatTaskThread.start();
}

public HeartBeatTask(long aHeartBeatInterval, Semaphore semaphore){
    this.heartBeatInterval = aHeartBeatInterval;
    this.semaphore = semaphore;
}

@Override
public void run() {
    while(isKeepHeartBeat()){
        System.out.println("beating");
        try {
            Thread.sleep(this.heartBeatInterval);
            semaphore.acquire(); // we acquire then immediately release
            semaphore.release(); // so that if the other thread has
                                 // already acquired it we'll block (so not beat).
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

Upvotes: 0

pimaster
pimaster

Reputation: 1967

Unfortuntely, you have more than just a beating/non beating state.

public class test
{
    public static void main(String[] args) throws Exception
    {
        HeartBeat hb = new HeartBeat();
        Thread t = new Thread(hb);
        t.start();
        Thread.sleep(1500);
        hb.setPaused(true);
        Thread.sleep(1500);
        hb.setPaused(false);
        Thread.sleep(1500);
        hb.stopBeating();
        t.join(); // make sure the thread terminates correctly;
        System.out.println("Patient is dead.");
    }
}

class HeartBeat implements Runnable
{
    private final Object lock = new Object();
    private volatile boolean paused = false;
    private volatile boolean running = true;

    public void run()
    {
        while (running)
        {
            if(paused)
            {
                synchronized (lock)
                {
                    try
                    {
                        while (paused)
                        {
                            lock.wait();
                        }
                    }
                    catch (InterruptedException e)
                    {
                    }
                }
            }
            else
            {
                try
                {
                    Thread.sleep(500);
                }
                catch (InterruptedException e)
                {
                }
                System.out.println("Beat");
            }
        }
    }

    public void stopBeating()
    {
        synchronized (lock)
        {
            running = false;
            paused = false;
            lock.notifyAll();
        }
    }

    public void setPaused(boolean paused)
    {
        synchronized (lock)
        {
            this.paused = paused;
            lock.notifyAll();
        }
    }
}

You need to be able to pause the thread, in this case I am just using the Object.wait() and Object.notify().

Other options could include using a Thread pool and re-adding this task to the pool when you want to start the job again. You just need to make sure that you don't add the task twice as you'd be beating twice as often as necessary.

Since you are writing something that gets executed every xxxx seconds, you might want to check out the ScheduledExecutorService. Pausing/stopping would involve canceling the task, resuming would be a matter of re-adding the task.

Upvotes: 1

Java42
Java42

Reputation: 7706

Another simple solution is to let the thread die on stop and create a new thread on resume. Use a thread pool if you do this (optional and not necessary in a small standalone app).

Upvotes: 0

Baptiste Gousset
Baptiste Gousset

Reputation: 251

One solution would be to use wait() and notify()

Upvotes: 2

Related Questions