org.feistymeow.process.ethread.java Source code

Java tutorial

Introduction

Here is the source code for org.feistymeow.process.ethread.java

Source

package org.feistymeow.process;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * A simple java thread that hearkens back to the HOOPLE C++ ethread in features.
 * 
 * @author Chris Koeritz
 * @copyright Copyright (c) 2010-$now By Feisty Meow Concerns Ltd.
 * @license This file is free software; you can modify and redistribute it under the terms of the Apache License v2.0:
 *          http://www.apache.org/licenses/LICENSE-2.0
 */
public abstract class ethread implements Runnable {
    private static Log c_logger = LogFactory.getLog(ethread.class);

    // the actual java thread object.
    private volatile Thread c_RealThread = null;
    // provides synchronization for the thread.
    private volatile Object c_lock = new Object();
    // this is the timing period, for a timed thread. if zero, then this is a single shot thread.
    private long c_period = 0;
    // records whether the thread should shut down or not.
    private boolean c_stopThread = false;
    // snooze between checks on the stop timer.
    final long SNOOZE_PERIOD = 20;

    /**
     * creates a new single-shot ethread without starting it. this type of thread will run just once.
     */
    public ethread() {
    }

    /**
     * creates a new periodic ethread without starting it. this type of thread runs every "period" milliseconds until stopped or until the
     * performActivity method returns false.
     */
    public ethread(long period) {
        c_period = period;
    }

    /**
     * this is the main function that derived classes must implement. it does the actual work that the thread is intended to perform. note
     * that the derived version must not do anything to cause the thread to be ripped out while performActivity is still being invoked. the
     * return value should be true if the thread can continue executing. this is meaningless for single shot threads executed via runOnce, but
     * matters for the periodic threads started with runPeriodic.
     */
    abstract public boolean performActivity();

    /**
     * Begins execution of the thread.
     */
    public void start() {
        synchronized (c_lock) {
            if (null == this.c_RealThread) {
                this.c_RealThread = new Thread(this);
                c_logger.debug("starting thread " + c_RealThread.getId());
                this.c_RealThread.start();
            }
        }
    }

    /**
     * Stops execution of the thread, or at least attempts to.
     */
    public void stop() {
        cancel();
        while (true) {
            if (threadAlive()) {
                try {
                    Thread.sleep(40);
                } catch (InterruptedException e) {
                    // ignoring this since we'll keep snoozing as needed.
                }
            } else {
                break;
            }
        }
    }

    /**
     * Signals the thread to stop executing, but does not wait for it.
     */
    void cancel() {
        synchronized (c_lock) {
            c_stopThread = true;
            Thread goAway = c_RealThread;
            c_RealThread = null;
            if (null != goAway) {
                goAway.interrupt();
            }
        }
    }

    /**
     * Returns true if the thread object is still alive. this does not necessarily mean it is currently active.
     */
    public boolean threadAlive() {
        synchronized (c_lock) {
            return this.c_RealThread != null;
        }
    }

    /**
     * returns true if the thread has been told to stop running.
     */
    public boolean shouldStop() {
        synchronized (c_lock) {
            return c_stopThread;
        }
    }

    /**
     * this is the override from Runnable that allows us to call our own performActivity method. implementors should not override this; they
     * should override performActivity instead.
     */
    @Override
    public void run() {
        if (!threadAlive()) {
            return; // stopped before it ever started. how can this be? we just got invoked.
        }
        try {
            while (true) {
                boolean keepGoing = performActivity();
                if (!keepGoing) {
                    c_logger.debug("thread returned false, signifying it wants to exit.  now dropping it.");
                    break;
                }
                if (c_period == 0) {
                    // not a periodic thread, so we're done now.
                    break;
                }
                long nextRun = System.currentTimeMillis() + c_period;
                while (System.currentTimeMillis() < nextRun) {
                    if (shouldStop()) {
                        break;
                    }
                    try {
                        Thread.sleep(SNOOZE_PERIOD);
                    } catch (InterruptedException e) {
                        // well, we'll hit it again soon.
                    }
                }
            }
        } catch (Throwable t) {
            c_logger.info("exception thrown from performActivity: " + t.getLocalizedMessage(), t);
        }
        // reset the thread held since we're leaving right now.
        c_stopThread = true;
        c_RealThread = null;
    }
}