Introduction to Threads

Modern computers have the capability to execute multiple tasks at the same time. Have you ever wondered, how it is achieved in the modern computers only, while older batch processing systems were unable to do so? Early days, processors were able to execute only one process at a time. Programmers fed the problems one after another in a queue to the computer. However, these days we are able to edit a text file while listening a song. In fact still processors can execute a single task at a specific time. Multicore processors have to be considered a set of sub processors; in that case, those sub processors can do one job at a time. Processors are using a technique called time sharing to execute multiple processes at the same time. The basic idea is executing every processes for a limited amount of time only. For example if you are playing a music in VLC player and reading this blog in Firefox, in background your processor is executing the VLC player for few milliseconds and then executing the Firefox for few milliseconds. Not only these two visible applications, all the invisible operating system related processes also running in the same manner. This execution happens cyclic through all the available threads in the system. Humans cannot feel the idle time of processes, because of the high frequency of processors.
What is a process and a thread? An executing application is called as a process. A process will get a fixed size of memory allocated by the operating system. Threads are small unit of a process which can execute simultaneously and share the memory of their parent process. For example when you are executing VLC and Firefox they are two processes. Within Firefox, you can play a video clip in a tab and view this blog in another tab. In this case there is a concurrent execution within a process. This concurrency is achieved by using multiple threads within a process.


Operating systems maintain a priority queue and all the threads which are waiting to access the processor will be in that queue. The first thread in the queue or a thread with higher priority than others (Usually system threads have higher priority than others) gets the first chance to use the processor. After a specific time, operating system saves the current state of executing thread into the memory, put it into the queue and select the next thread for execution. This circle continues for every threads until they complete their assigned task. When a thread gets the second chance to execute, operating system will restore its last known state and the thread will continue from where it left over. Once the assigned task of a thread has been completed or if the threads has been terminated by operating system, the thread will be permanently removed from the thread queue and memory. Sometimes a thread needs to wait for any events. For example, you may have experience with modern antiviruses, where whenever a new pen drive is detected the antivirus shows a pop up to scan the drive or not. If a thread wants to execute a specific action on USB arrival, it does not need to check for USB drives continuously, instead it can inform the operating system to notify it when there is a new USB drive detected and until that the thread can wait in a special memory area called waiting pool.

Life Cycle Of Threads
As you can see, there are various states in a thread's life cycle. A thread is considered as a running thread if it is currently running inside the processor; if it is waiting in the thread queue, it is called as runnable thread. A thread which is waiting in the waiting pool is in the waiting/blocked state. Once a thread is terminated either by timeout or by operating system, it is dead. A thread that is neither in the waiting pool nor in the thread queue but in the memory (Just the thread object is created but not started to execute) is defined as a new thread.

Create And Run Threads
All the Java applications have a default main thread to execute its main method. If the developer needs to create any additional threads, he/she has to create a thread object and start it using Thread.start() method. A thread class can be created either by implementing java.lang.Runnable interface or by extending java.lang.Thread class. Using the thread class any number of thread objects can be created. Following example shows sample thread classes using these two ways.
public class ThreadA extends Thread {
 @Override
 public void run() {
  for(int i = 0; i < 1000; i++) {
   System.out.println("Thread A: " + i);
  }
 }
}
public class ThreadB implements Runnable {
 @Override
 public void run() {
  for(int i = 0; i < 1000; i++) {
   System.out.println("Thread B: " + i);
  }
 }
}
Once the object is created, the thread has to be started using Thread.start() method. For the Runnable object, since there are no start method in the Runnable object a helper thread object is needed to start the Runnable object. Following code is used to start newly created threads.
public class ThreadDemo {
 public static void main(String[] args) {
  // Create thread objects
  Thread threadA = new ThreadA(); // Thread object
  Runnable runnable = new ThreadB(); // Runnable object
  Thread threadB = new Thread(runnable); // Helper thread object

  // Start the threads
  threadA.start();
  threadB.start();
 }
}

Sleep and Wakeup
Threads can sleep for a given time without consuming the processor. It is commonly used to perform any timer events. For example you need to print a message every 2 seconds. You can use sleep method as a way to wait for 2 seconds, but remember that it cannot be used for any precise timers where you need to execute something exactly on time.
public class TimerDemo {
 public static void main(String[] args) {
  Runnable timer = new Timer();
  Thread thread = new Thread(timer);
  thread.start();
 }
}

class Timer implements Runnable {
 private final long INTERVAL = 2000; // 2 seconds
 @Override
 public void run() {
  while(true) {
   // Print the message
   System.out.println("Hello world!");
   // Sleep for 2 seconds
   try {
    Thread.sleep(INTERVAL);
   } catch(InterruptedException ex) {}
  }
 }
}

Waiting For Another Thread
A thread can wait for another thread, if it cannot starts its task until the desired thread terminates. For example assume that there are two threads, Mason and Painter. Here Painter cannot starts his job until Mason completes his task. It can be implemented using join method of thread class. threadA.join(); means the currently executing thread (The thread which is executing the join method) will wait until thread A terminates.
public class JoinDemo {
    public static void main(String[] args) {
     Thread mason = new Thread(new Mason());
     Thread painter = new Thread(new Painter(mason));
     mason.start();
     painter.start();
    }
}

class Mason implements Runnable {
    @Override
    public void run() {
     System.out.println("Start building.");
        for (int i = 0; i < 5; i++) {
            System.out.println("Building a house...");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException ex) {}
        }
        System.out.println("Finish building.");
    }
}

class Painter implements Runnable {
    private Thread mason;

    public Painter(Thread mason) {
        this.mason = mason;
    }

    @Override
    public void run() {
     System.out.println("Wait for mason.");
     // Join this thread after the mason thread.
        try {
            this.mason.join();
        } catch (InterruptedException ex) {}
        System.out.println("Start painting.");
        for (int i = 0; i < 5; i++) {
            System.out.println("Painting the house...");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException ex) {}
        }
        System.out.println("Finish painting.");
    }
}


Find the source codes at Git Hub.
Previous
Next Post »

Contact Form

Name

Email *

Message *