Java Worker Thread Example

0

In this article we will see an example of a worker thread. When a work thread is created and run, it loops and waits for the availability of a task to execute. Once a task is assigned, it comes out of the waiting loop and executes the task. After the task is run, it will again wait for a new task, it will periodically check for the availability of task every few milliseconds.

Task Thread Flow

A task thread is a responsible to execute a single task when assigned to it. Once executed, it is ready for another task. If we try to assign another task while it is still busy with the current task then it will throw an exception ‘Already running a task!’.
A task thread can be used as a single worker thread and one can create a pool of worker threads, this way we can take care of multiple tasks.

Single Worker Thread Flow

Single Task Thread Flow

When a task thread is started, it waits in loop till a task is assigned. By default the task thread is in a state to consume tasks based on the run atomic boolean. As long as this is true the thread is always running. In the below diagram this is shown as the outer loop. The inner loop is based on whether there is a task available.
There are two important methods:

  1. Method run(Runnable) is called to assign a task to the thread for execution. Once assigned the lock is notified.
  2. Method stopTaskThread() sets the flag run to false. First the thread will come out of inner loop and then the outer loop.

TaskThread:

package com.javarticles.threads;

import java.util.concurrent.atomic.AtomicBoolean;
import static com.javarticles.threads.PrintUtil.*;

public class TaskThread extends Thread {
    private AtomicBoolean run = new AtomicBoolean(true);
    private Runnable runnable;
    private final Object lock = new Object();
    
    TaskThread() {
       super("TaskThread");
    }

    public void run(Runnable newRunnable) {
        synchronized(lock) {
            print("Got the lock. Assign new task and notify task thread");
            if(runnable != null) {
                throw new IllegalStateException("Already running a task!");
            }           
            runnable = newRunnable;
            lock.notifyAll();
        }
    }
    
    public void stopTaskThread() {
        synchronized (lock) {
            print("Stop task thread");
            run.set(false);   
        }        
    }
    
    public void run() {
        boolean ran = false;
        while (run.get()) {
            print("In task thread, look for new tasks");
            synchronized (lock) {
                print("Got the lock. Check whether it needs to run any task");
                try {
                    waitForRunnable();
                    ran = executeRunnable();
                } catch (Throwable exceptionInRunnable) {
                    print("Error while executing the Runnable: " + exceptionInRunnable);                    
                } finally {
                    cleanupRunnable();
                    if (ran) {
                        ran = false;
                    }
                }
            }
        }
        print("Task thread is down");
    }
    
    private boolean executeRunnable() {
        if (runnable == null) {
            return false;
        }
        print("Got a new task to run");
        print("Run the task");
        runnable.run();
        return true;        
    }
    
    private void cleanupRunnable() {
        synchronized (lock) {
            print("Task execution over");
            runnable = null;
        }
    }

    private void waitForRunnable() {
        while (runnable == null && run.get()) {
            print("No task, wait for 500ms and then check again");
            try {
                lock.wait(500);
            } catch (InterruptedException e) {
                print("Task thread was interrupted." + e);
            }
        }
    }
}

We want to print the thread name too so PrintUtil class appends the thread name.

PrintUtil:

package com.javarticles.threads;

public class PrintUtil {    
    private static Thread t() {
        return Thread.currentThread();
    }
    
    public static void print(String text) {
        System.out.println(t() + ":" + text);
    }
}

In the main thread, we create instance of task thread an dstart it. Next we assign a task to the thread for execution. If we try to assign another task it is going to throw IllegalStateException. Note if you want to execute multiple tasks then we need to maintain a pool of task threads. After waiting for few milliseconds we assign another task for execution. Next we wait for few more seconds and then stop the task thread.

SingleTaskThreadExample:

package com.javarticles.threads;

import static com.javarticles.threads.PrintUtil.*;

public class SingleTaskThreadExample {
    public static void main(String[] args) {
        TaskThread taskThread = new TaskThread();
        taskThread.start();
        taskThread.run(new Task());
        try {
            taskThread.run(new Task());
        } catch (IllegalStateException e) {
            print(e.toString());
        }
        waitTime(1000);
        taskThread.run(new Task());
        waitTime(3000);
        taskThread.stopTaskThread();
    }
    
    private static void waitTime(long ms) {
        try {
            Thread.sleep(ms);
        } catch (InterruptedException e) {
            print("Thread interrupted");
        }
    }
    
    static class Task implements Runnable {
        static int i = 0;
        Task() {
            i++;
        }
        public void run() {
            print("Task" + i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {                
                e.printStackTrace();
            }
        }
        
    }
}

The output below is self explanatory.

Output:

Thread[main,5,main]:Got the lock. Assign new task and notify task thread
Thread[TaskThread,5,main]:In task thread, look for new tasks
Thread[main,5,main]:Got the lock. Assign new task and notify task thread
Thread[TaskThread,5,main]:Got the lock. Check whether it needs to run any task
Thread[main,5,main]:java.lang.IllegalStateException: Already running a task!
Thread[TaskThread,5,main]:Got a new task to run
Thread[TaskThread,5,main]:Run the task
Thread[TaskThread,5,main]:Task2
Thread[TaskThread,5,main]:Task execution over
Thread[TaskThread,5,main]:In task thread, look for new tasks
Thread[main,5,main]:Got the lock. Assign new task and notify task thread
Thread[TaskThread,5,main]:Got the lock. Check whether it needs to run any task
Thread[TaskThread,5,main]:Got a new task to run
Thread[TaskThread,5,main]:Run the task
Thread[TaskThread,5,main]:Task3
Thread[TaskThread,5,main]:Task execution over
Thread[TaskThread,5,main]:In task thread, look for new tasks
Thread[TaskThread,5,main]:Got the lock. Check whether it needs to run any task
Thread[TaskThread,5,main]:No task, wait for 500ms and then check again
Thread[TaskThread,5,main]:No task, wait for 500ms and then check again
Thread[TaskThread,5,main]:No task, wait for 500ms and then check again
Thread[TaskThread,5,main]:No task, wait for 500ms and then check again
Thread[main,5,main]:Stop task thread
Thread[TaskThread,5,main]:Task execution over
Thread[TaskThread,5,main]:Task thread is down

Download the source code

This was an example about a worker thread to execute tasks.

You can download the source code here: javaSingleWorkerThreadExample.zip

About Author

Ram's expertise lies in test driven development and re-factoring. He is passionate about open source technologies and loves blogging on various java and open-source technologies like spring. You can reach him at rsatish.m@gmail.com

Comments are closed.