Threads and Processes

What is a thread ?

A thread is a kernel abstraction for scheduling work on the CPU. A thread is essentially an execution context which a CPU needs to execute a bunch of instructions. As a bare minimum coding construct, any thread implementation will have the following associated context :

  • Thread id
    This is typically a numeric value which uniquely identifies a thread
  • Program counter
    This is a pointer to the current instruction in memory that is being executed by the processor. Once an instruction is executed, the program counter is altered to point to the next instruction to be executed and so on. Since each thread represents an independent path of execution, each thread will have it's own program counter
  • Register set
    They essentially hold a thread's execution context, so that, if a thread loses CPU attention and gains it back at a later point of time, it can proceed with the same contextual information
  • Stack
    A thread's stack is part of it's process' allocated address space. Each entry in the stack is called a Stack Frame. Each stack frame corresponds to an unfinished method execution. When a method execution is complete, the corresponding entry is removed the stack. Stack Frame contains the states of all the local variables involved.

How is a thread different from a process ?

A process refers to an executing program. A process is an instance of a program execution. Just a thread, every process has its unique (typically numeric) identifier. A process can also be thought of as an abstraction for a number of threads running within it, each performing a unit of work. A live process will have at least one live thread. Unlike threads, each process has its own address space. While threads within a process can communicate through shared memory, processes are not allowed to share memory and can only communicate through inter-process-communication techniques (which often require explicit support from underlying operating system)

Scheduling and Context-switching

Let's consider a single core/single CPU system. Typically there would be multiple processes running within a system. In order to create the illusion of multiple processes running 'simultaneously' and also to optimize performance, it is important that every thread gets some share of CPU attention. This process of deciding which thread should run when is called 'scheduling' in operating system parlance. A very simplistic implementation of scheduling is 'round robin scheduling' where the scheduler maintains a queue (typically called 'run queue' or 'ready queue') of currently runnable threads with the first item on the queue being the currently running thread. After a while (a period of time called the scheduling quantum), the scheduler suspends the running thread, puts it at the back of the queue and picks the next thread on the queue to run. Newly created threads are appended to the back of the queue. This ensures that every thread gets a fair share of the CPU over a period of time. Of course, this is a very simplistic view and modern day scheduling algorithms take a lot of additional factors into consideration.

Context switching is the act of suspending/pausing the execution of one thread while starting/resuming the execution of another. Context switching involves:
1. selecting the next thread to run
2. saving the context of currently-running and about-to-be-paused thread
3. loading the context of about-to-be-resumed thread

Clearly context switching is an expensive (resource and time consuming) operation. If there are too many threads running simultaneously, the benefits of multithreading can easily be wiped off by excessive context switching.

Why do we need to explicitly create threads ?

Strictly speaking, we don't need to. When we start a Java application, JVM creates the main thread which runs our main() method That's what we would call a single threaded application (of course there are JVM initiated background threads like GC threads, but for now we'll ignore them). However most modern day machines where we run our applications are multi processor/multi core systems. So we can take advantage of that my running multiple threads simultaneously. Even in a single processor system, a thread doesn't need CPU attention throughout the duration of its run - as part of it's execution it might be performing non computation tasks like IO operations or might even be blocked waiting for some event to occur. Running multiple threads can help us improve our throughput (We'll discuss more about it when we talk about Amdahl's law).

There are situations though where you are better off letting the application container manage threads and not create new threads explicitly as part of application logic. Spawning new threads is normally (By "normally" I mean "while developing business applications") discouraged when you are within a Java EE container (Infact EJB 2.1 and later specifications prohibit the creation of explicit non-container-managed threads)

Also note that Java threads need to mapped to native OS threads. There are several models for mapping. The two most extreme ones are :

Green Threads
All Java Thread instances are managed within one native OS thread.
Native Threads
Each Java Thread instance is backed by a native OS thread

Because of performance limitations of green threads, almost all modern JVM implementations, including HotSpot, choose the later implementation. This implies that the OS needs to reserve some memory for each created thread. Also, there is some runtime overhead for creating such a thread as it needs direct interaction with the underlying OS. At some point, these costs accumulate and the OS refuses the creation of new threads to prevent the stability of your overall system. Threads should therefore, ideally, be pooled for re-use.

Creating and starting a new thread in Java

java.lang.Thread class instances represent a thread in Java. The start() method of the Thread class is used to start a new thread in Java - it causes JVM to execute the run() method of the same object in a new thread. To create a custom thread one has to subclass java.lang.Thread and provide an implementation to the run() method

    Thread t = new Thread() {
        public void run() {
            System.out.println("Hello world");
        }; 
    };
    t.start();

Alternately, we can use the Runnable interface. Runnable is a functional interface which allows implementing class to act like an executable task without having to extend Thread. A thread object can be associated with a Runnable instance by passing the Runnable instance as part of the thread's constructor arguments.

    Runnable r = new Runnable() {

        @Override
        public void run() {
            System.out.println("Hello world");
        }
    };
    new Thread(r).start();

By default, the run method of Thread executes the run method of the associated Runnable instance (if there is one), else it does nothing

@Override
public void run() {
    if (target != null) {
        target.run();
    }
}

Note that a thread can be started only once. Re-attempting to start an already started thread will result in IllegalThreadStateException

    Thread t = new Thread() {
        public void run() {
            System.out.println("Hello world");
        }; 
    };
    t.start();
    t.start();

results in

Exception in thread "main" Hello world
java.lang.IllegalThreadStateException
  at java.lang.Thread.start(Thread.java:708)

Also once thread has finished running, it cant be restarted or reused again. Invoking start() on a dead thread will result in IllegalThreadStateException

public static void main(String[] args) throws Exception {        
    Thread t = new Thread() {
        public void run() {
            System.out.println("Hello world");
        }; 
    };
    t.start();
    TimeUnit.SECONDS.sleep(1);
    // by now the thread t has finished execution        
    t.start();
}

results in

Hello world
Exception in thread "main" java.lang.IllegalThreadStateException
    at java.lang.Thread.start(Thread.java:708)

Thread id

Every thread has a unique identifier (of type long) called thread id. Thread id is a positive long value which remains unchanged during the lifetime of a thread

At present the thread id implementation is based off a numeric sequence which is incremented by 1 every time a new thread is created. However, it's an internal implementation and we should never write production quality code which is based on implementation logic for thread id

private static synchronized long nextThreadID() {
    return ++threadSeqNumber;
}

JVM guarantees that at any point of time, no two live threads will have the same id. JVM specifications also state that it is possible that after a thread dies, the id is reused by another thread

We can use the following public method to access a thread's id:

public long getId()

Food for thought : If thread id remains constant throughout the lifetime of a thread, why isn't it declared as final ? (Note that threads are assigned ids at construction time, so even if a thread isn't started, it will still have an id)

For purely legacy reasons.

There was actually a Jira item for Open JDK 5 to change this to final Check this link. However it wasn't accepted as adding a final method to an existing class results in binary incompatibility with earlier versions.

Daemon and Non daemon thread

Java supports two types of threads - daemon and non-daemon. A daemon thread is one which cannot prevent JVM shutdown. JVM continues to run as long as there is at least one non-daemon thread running. If all the currently alive threads are daemon threads, JVM initiates shut down process.

A thread can be marked as daemon (or not) by calling the following method on the thread object :

public final void setDaemon(boolean on)

The daemon flag has to be set before a thread is started. Once a thread has been started, the daemon flag can't be altered and any attempt to do so will result in IllegalThreadStateException

Note that a thread automatically inherits daemon status from its parent thread. However, it can be altered by explicitly calling setDaemon(boolean on)

To check if a thread is a daemon thread or not, we can use the following method :

public final boolean isDaemon()

Thread priority

Every thread is associated with a priority value. Priority is an integer ranging between 1 (minimum) and 10 (maximum). The main thread has a priority of 5 (which is the default priority)

public final static int MIN_PRIORITY = 1;

public final static int NORM_PRIORITY = 5;

public final static int MAX_PRIORITY = 10;

Just like the daemon flag, a thread inherits priority from its parent thread. However, priority can be set explicitly by invoking the following method on the thread instance :

public final void setPriority(int newPriority)

Note that if the thread is already associated with a thread group which has a max priority set, we cant use the setPriority method to set a higher priority value (than the max priority of the thread group). The priority is always set to the smaller of the two values :

  • The explicit value passsed as argument to setPriority method

  • The max priority of the thread group associated with this thread

e.g The following code will print the thread priority as 7 and not 9 :

    ThreadGroup group = new ThreadGroup("dummy-group");
    group.setMaxPriority(7);

    Thread t = new Thread(group, "mythread") {
        public void run() {
            System.out.println("Hello world");
        }; 
    };
    t.setPriority(9);
    System.out.println(t.getPriority());

To access a thread's priority, we can use the following method:

public final int getPriority()

Threads with higher priority should ideally be preferred (by scheduler) over threads with lower priority. However the actual behavior is highly platform specific and we should never write production quality code that relies on thread priority for correctness - that's partly because the mapping between Java's thread priorities and OS native thread priorities are platform specific and can vary between JVM implementations

On Windows

On Windows, there are 5 different native thread priority levels :
THREAD_PRIORITY_LOWEST
THREAD_PRIORITY_BELOW_NORMAL
THREAD_PRIORITY_NORMAL
THREAD_PRIORITY_ABOVE_NORMAL
THREAD_PRIORITY_HIGHEST

The 10 Java priority levels map to OS native priority levels like this :
1-2: THREAD_PRIORITY_LOWEST

3-4: THREAD_PRIORITY_BELOW_NORMAL

5-6: THREAD_PRIORITY_NORMAL

7-8: THREAD_PRIORITY_ABOVE_NORMAL

9-10: THREAD_PRIORITY_HIGHEST

On UNIX

On UNIX native thread priorities are represented by numbers ranging from +19 to -20. These numbers are also called nice numbers (nice is a UNIX built-in tool used to invoke a shell script with a particular priority). A niceness of −20 is the highest priority and 19 is the lowest priority (lower the niceness, higher the priority). The default niceness for processes is inherited from its parent process and is usually 0.

The Java priorities 1-10 are mapped to nice numbers +4 to -5

From the two examples above we can clearly see that while Windows doesn't distinguish between a priority of 7 and 8, in UNIX they are interpreted as different Native thread priorities. So the same code might behave differently on Windows and UNIX. As a rule of thumb, if your code correctness relies on priority, you are probably doing something wrong. Even if you don't plan to run it in multiple platforms, upgrading your JRE or applying a patch/service pack to your OS could break it.

Also note that because threads might depend on each other, making a higher priority thread dependent on a lower priority thread will automatically lead to 'priority inversion'. That's another reason why you should not rely on priority for correctness.

Interrupt

Interrupt is a process of notifying a thread that it should stop doing what it is currently doing.

To interrupt a thread, we can invoke the interrupt method :

public void interrupt(

Note: it's absolutely legal for a thread to interrupt itself

Calling interrupt on a thread, sets the interrupt flag for the thread instance.

To check if the interrupt flag is set or not, we can use either of the following methods :

public static boolean interrupted()

public boolean isInterrupted()

Note that there is a subtle difference between the two methods - the non static method is idempotent whereas the static method is not.

The non static method, simply returns a boolean value indicating whether the interrupt flag is set or not.

The static method checks whether the interrupt flag is set or not for the current thread, clears the flag and returns a boolean value
indicating whether the interrupt flag was set or not. So, assuming nothing else happened in between, if we make two successive calls to the interrupted() method, the second one will always return false (because the first call has reset the flag already)

Also note that if a thread goes into blocked or sleeping state with the interrupt flag still set (or is interrupted while being blocked or sleeping), it will throw InterruptedException
A thread can go into blocked or sleeping state in any of the following circumstances:
- It invoked wait (or its timeout variant methods) on an object
- It invoked join (or its timeout variant methods on a thread)
- It invoked sleep()
- It's blocked in an I/O operation upon an interruptible channel

Note that interrupting an already dead thread will have no effect - it won't even set the interrupt status flag for that thread

A typical use-case for interrupts is to provide some kind of hook into a thread using which we can prevent a thread from running forever

Whats a good way to handle InterruptedException ?

InterruptedException is among the most misunderstood exceptions in Java. If you deal with a lot of multi-threaded Java code, chances are very high that you have come across production deployed code like this :

    try {
        TimeUnit.SECONDS.sleep(5);
    } catch (InterruptedException e) {
        // do nothing
    }

or even this:

    try {
        TimeUnit.SECONDS.sleep(5);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }

Needless to say, on most occasions, exception handling logic like this is definitely improper. Interrupt is a request to a thread to halt its execution and ideally the code being executed within the thread should detect and respond to such requests

Let's say we have a long running task - for example, say, a thread iterates over an infinite while loop and polls for new entries made to database and if it finds an entry processes it.

A good way to handle interrupts in scenarios like this is :

    while(!Thread.currentThread().isInterrupted()) {            
        // do something
    }

But what happens when the thread is in one of the states BLOCKED, WAITING or TIMED_WAITING when it is interrupted ?

Let's look at our options here :

Catch and ignore : definitely not recommended

Catch and propagate : Since InterruptedException is a checked exception and run() method of Runnable/Thread doesn't allow us to throw checked exceptions, only option here is to catch it, wrap it into a RuntimeException and throw again. While this is better that swallowing the Exception, the exception would end up in console (or if there is an uncaught exception handler, whatever action is taken by the uncaught exception handler). It achieves its purpose (of terminating the thread) but not in a very clean or traceable way

Catch, restore and terminate : Catch InterruptedException, restore the interrupt status flag (Note that when InterruptedException is thrown, the interrupt status flag is cleared) and have logic in place to terminate processing (and cleanup resources if any). Something like this :

    while(!Thread.currentThread().isInterrupted()) {            
        // do something
        try {
            TimeUnit.SECONDS.sleep(10);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }

Note that Java 1,5 onwards, shutdownNow() method in executors, rely on interrupts to terminate the threads. So if threads don't respond to interrupts, they will never be terminated. In other words, threads are not cancellable if they are executing code that doesn't respond to interrupts.

sleep

A thread will go to sleep for a certain duration if it invokes any of the following static methods on Thread class:

public static native void sleep(long millis) throws InterruptedException;
public static void sleep(long millis, int nanos) throws InterruptedException

If a thread has interrupt flag set while invoking any of these methods, it will throw InterruptedException.

Also once a thread has invoked Thread.sleep(milis) or Thread.sleep(milis, nanos) and the sllep method hasnt returned yet (i.e. the thread is still in sleeping state), another thread calling interrupt() on this thread instance will result in InterruptedException

Note: A thread doesn't lose it's current monitors/locks when it goes into sleeping state

yield

public static native void yield();

Yield is a suggestion to thread scheduler that the current thread is willing to temporarily relinquish it's current use of a processor in favor of another (possibly higher priority) thread

Note that this is just a suggestion and thread scheduler is free to ignore it.

Just like priority, if a piece of code relies on yield() for correctness, it is as good as broken.

sample usage:

public static void main(String[] args) throws Exception {

    Runnable r = new Runnable() {
        public void run() {
            System.out.println(Thread.currentThread().getName() + " yielding ...");
            Thread.yield();
            System.out.println(Thread.currentThread().getName() + " resumed");
        }; 
    };

    new Thread(r, "Thread-1").start();
    new Thread(r, "Thread-2").start();

}

output:

Thread-2 yielding ...
Thread-1 yielding ...
Thread-2 resumed
Thread-1 resumed

join

join is a mechanism to make a thread wait until another thread has died (or a specific amount of time has elapsed). So if thread A invokes join on thread B, thread A is blocked until thread B finishes execution (or timeout occurred). There are 3 overloaded versions of join method available in Thread class. The signatures are self explanatory and are indicative of the expected behavior

public final void join() throws InterruptedException
public final synchronized void join(long millis) throws InterruptedException
public final synchronized void join(long millis, int nanos) throws InterruptedException

Just like sleep, join responds to an interrupt by exiting with an InterruptedException

Interestingly the timed versions of the join method are synchronized while the no-timeout version is not. That's because join() internally invokes join(0) and is hence effectively synchronized. The purpose of synchronization here isnt to ensure mutual exclusion but to ensure that when a thread t1 calls t2.join(), then all changes done by t2 are visible in t1 on return (ie all cache writes are flushed out) - We'll cover this in more details in later sections

public static void main(String[] args) throws Exception {

    Runnable r = new Runnable() {
        public void run() {
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " is done");
        }; 
    };

    long start = System.currentTimeMillis();
    Thread t1 = new Thread(r, "T1");
    t1.start();
    t1.join();
    System.out.println("Call returned from join");
    long end = System.currentTimeMillis();
    System.out.println("Time taken by main thread :" + (end-start)/1000);

}

In the code snippet above the main thread creates another thread called T1 and joins on it.

T1 sleeps for 5 seconds before terminating.
Since main thread joins on T1, the execution time for main thread will always be >= T1 (even though main thread isn't doing anything)

Output of the above code:

T1 is done
Call returned from join
Time taken by main thread :5

Now lets change the code to use join with a timeout. We'll keep the timeout as < 5 seconds (since we know T1 takes at least 5 seconds to run). Now the execution time of the main thread will be almost same as the timeout period

modified code (we changed the call to join to use a timeout of 3 seconds):

public static void main(String[] args) throws Exception {

    Runnable r = new Runnable() {
        public void run() {
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " is done");
        }; 
    };

    long start = System.currentTimeMillis();
    Thread t1 = new Thread(r, "T1");
    t1.start();
    t1.join(3000);
    System.out.println("Call returned from join");
    long end = System.currentTimeMillis();
    System.out.println("Time taken by main thread :" + (end-start)/1000);

}

Output:

Call returned from join
Time taken by main thread :3
T1 is done

As expected, the time taken by main thread is now almost same as the timeout period.
Also note that Thread T1 finished after main thread (look at the relative order of line no 2 and 3 in the output)

thread state

A thread can be in one of the following states :

NEW

Thread object created but not yet started

RUNNABLE

A thread executing in the JVM (Also includes threads that are waiting to be serviced by scheduler). A NEW thread enters the RUNNABLE state when you call Thread.start() on it

BLOCKED

A thread waiting for a monitor

WAITING

A thread waiting indefinitely for another thread to perform some action

TIMED_WAITING

Same as WAITING except that there is a timeout period

TERMINATED

A dead thread (completed the execution of its Runnable.run() method)

The following diagram explains the transition between different thread states :
![alt txt] (
https://www.uml-diagrams.org/examples/state-machine-example-java-6-thread-states.png
"Thread State Diagram")

To get a thread's current state:

public State getState()

Uncaught Exception Handler

Sometimes, we may run into a scenario whether a part of the code throws a RuntimeException (or its subclass) which isn't caught by any of the catch blocks. The exception propagates all the way to the main() method ( or to run() method if it's thrown from a child thread ) and can only be found on the console (which is typically flushed out on reboot) and not on any log file which makes it very difficult to detect or investigate the issue. To avoid this we have the Uncaught Exception Handler.

@FunctionalInterface
public interface UncaughtExceptionHandler {
    void uncaughtException(Thread t, Throwable e);
}

The implementations of this interface are expected to provide handlers when a Thread abruptly terminates due to an uncaught exception

An implementation of UncaughtExceptionHandler can be made to associate with a thread through the following method :

public void setUncaughtExceptionHandler(UncaughtExceptionHandler eh)

When a thread is about to terminate due to an uncaught exception the JVM will query the thread for its UncaughtExceptionHandler and will invoke the handler's uncaughtException method, passing the thread and the exception as arguments.

If a thread has not had its UncaughtExceptionHandler explicitly set, then its ThreadGroup object acts as its UncaughtExceptionHandler (ThreadGroup implements Thread.UncaughtExceptionHandler)

There's also a Default UncaughtExceptionHandler - applicable to all threads there there's no thread level or thread group level UncaughtExceptionHandler. The default UncaughtExceptionHandler can be set by calling the following static (as it is not applicable to any particular thread) method of Thread class

public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh)

Note that the default uncaught exception handler should not defer to the thread's ThreadGroup object or thread's uncaught exception handler object, as that could cause infinite recursion.

Thread Group

A Thread Group is essentially an abstraction to deal with a group of threads in a generic and collective way. It's a tree-like structure that can include one or more threads and/or other threadgroups as well.

A thread group is made to associate with a thread by passing the thread group object as part of the thread's constructor

public Thread(ThreadGroup group, String name)
public Thread(ThreadGroup group, Runnable target, String name)

A thread can access its ThreadGroup by using the following method :

public final ThreadGroup getThreadGroup()

Note that once a thread dies, is removed from the group ( in other orders association with a ThrteadGroup doesnt prevent a dead Thread object from being garbaged collected). As a result, invoking getThreadGroup() on a dead thread will always return null

Every thread is associated with a thread group - either implicitly or explicitly. If no explicit thread group is specified, the thread group is determined by SecirityManager.getThreadGroup(). If SecurityManager.getThreadGroup() returns null, then the group is set to be the current thread's thread group.

Here are some of the things that we can do with thread groups :
setting max priority
We can set max priority for threads in a thread group using the following method :

public final void setMaxPriority(int pri)

The priority of the thread group is set to the minimum of the two values :

the argument passed to the setMaxPriority method

the max priority of the parent group

Once a thread is part of a thread group and max priority is set for the thread group, setting priority of the thread to a higher (than the max priority of the group) value will not have the desired effect. The priority of the thread will still be the max priority of the group and not the priority user tried to set.

Note that if there are threads already part of a thread group with higher priority, setting the max priority of the thread group would not impact priority of those threads

uncaught exception handler
ThreadGroup implements UncaughtExceptionHandler and hence proovides implementation to the following method :

public void uncaughtException(Thread t, Throwable e)

With the default implementation, a ThreadGroup first tries to invoke uncaughtException() on the parent thread group. If no parent is set (ie if its the top level ThreadGroup), it tries to invoke the uncaughtException method of the default uncaught exception handler

public void uncaughtException(Thread t, Throwable e) {
   if (parent != null) {
       parent.uncaughtException(t, e);
   } 
   else {
       Thread.UncaughtExceptionHandler ueh = Thread.getDefaultUncaughtExceptionHandler();
       if (ueh != null) {
           ueh.uncaughtException(t, e);
       } else if (!(e instanceof ThreadDeath)) {
           System.err.print("Exception in thread \"" + t.getName() + "\" ");
           e.printStackTrace(System.err);
      }
   }
}

Note that if you implement the Default Uncaught Exception Handler to use the thread group's uncaughtException method, it will result in an infinite recursion.

interrupt

A thread group can also be used to interrupt all the constituent threads and thread groups at one go

public final void interrupt()

Thread name

Every thread has a name. Name can be passed to a thread through its constructor or through the setName(String method). Unlike id, names are not expected to be unique however it's a good idea to provide them with unique names

Most loggers would generally put the name of the thread as part of every line of log. So, from a log investigation point of view it's generally a good idea to put explicit names.

To get reference to current thread

To get a reference to the currently executing thread, use :

public static native Thread currentThread();

comments powered by Disqus