Concurrency in Java: Creating and Starting a Thread

  • 4.7/5
  • 4823
  • Jul 20, 2024

Multithreading in Java means that there are multiple threads of execution inside the same application executing different parts of the code at the same time.

A thread in Java is an independent path of execution that does not run 'within', but rather side-by-side with another process or thread.

When we start the JVM, there is only one process java.exe or one thread, "main".

This "main" thread or "any thread" can start another thread, so long as the OS allows it.

In Java, a thread can be created in one of the following two ways:

Implementing the Runnable Interface

In this approach, we create an instance of java.lang.Thread class, passing a java.lang.Throwable instance in its constructor and call "start()" on it.

This thread, when started, will execute code written in the overridden run() method of implementation class.

If you call run() directly on the "java.lang.Throwable" instance, it will still execute code written in the run() method, but it will do so in the current thread (main) instead of inside a new thread.

Similarly, if you call run() instead of start() on the "java.lang.Thread" class object, no new thread will be created.

One advantage of creating a thread by implementing the Runnable interface is that it allows the implementing class to extend some other class. (Because Java only allows for one class extendability per class).

Another advantage of this approach is that it gives you an object that can be shared amongst multiple threads.

Using anonymous "Runnable"

A thread can also be started using an anonymous "Runnable" object, as shown below.

Using lambda expression

Moreover, the syntax of this anonymous "Runnable" can be made more crisp using lambda expressions.

Extending the Thread class

In this approach, we create a Java class extending the "java.lang.Thread" class. Now to start a thread, we create an instance of this sub-class and call the start() method on it.

If a run() method is overridden in the sub-class, the code inside that is executed by the thread, otherwise a default run() method defined in the "Thread" class is executed.

If you call run() on the sub-class instance directly, it will still execute code written in the run () method, but it will do so in the current thread (main) and no new thread will be created.

We cannot run "start()" on the same instance of a "Thread" more than once. Doing so will throw an "IllegalThreadStateException" exception.

One advantage of creating a thread by extending the Thread class is that it provides some inbuilt methods like yield(), interrupt (), etc. those are not available in the Runnable interface.

Using anonymous subclass of Thread

We can also start a new thread without creating a named subclass. We can do so by creating an anonymous subclass of Thread and override run() methods as show in the example below:

Java also provides an Executor framework that can be used to create thread pools for multi-threading.

Priority of a Thread

Java schedules different threads based on the priority of a thread, also known as preemptive scheduling.

In preemptive scheduling, the higher priority task will continue to receive execution time until it enters a waiting or dead state, or until a task with a higher priority enters the scheduling queue.

Another popular scheduling method is slicing scheduling, which gives every task a fair chance of being scheduled based on time slices.

In Java, every thread has a priority, specified as a number between 1 and 10.

A new thread gets the same priority as the priority of the thread that created it.

The default priority of a thread is 5 or NORM_PRIORITY.

We can set three priorities on a thread - MIN_PRIORITY or 1, NORM_PRIORITY or 5 and MAX_PRIORITY or 10.

Thread priority is only hint to OS task scheduler. Task scheduler will try to allocate more resources to thread with highest priority, but there are no explicit guarantees.

Process vs Thread

Process

A process is an unit of execution with its own "separate memory".

Each JVM (java application) runs as a "process" and has its own memory space known as a "heap" (heap cannot be shared by multiple java applications).

Thread

A thread is an unit of execution within a process. Each java process (application or JVM instance) has at least one "thread" (main).

Every thread created inside a process shares the thread's resources (memory and files).

In addition to shared memory (heap) of the process (JVM), every thread has its own non-sharable "thread stack (memory having local variables)" and "instruction pointer (address for next instruction to execute)"

Context switching

In concurrency, a context switch is the process of storing the state (local data, program pointer) of a thread, so that it can be restored and resume execution at a later point, and then restoring a different, previously saved, state.

This allows multiple threads to share a single CPU and is an essential feature of a multithreaded application.

Context switching isn't cheap. An application should not switch between threads more than necessary.

Thread Class Methods

Thread.currentThread()

The java.lang.Thread.currentThread() method returns a reference to the currently executing thread object.

We can get useful information like "getName()", "getId()" and "getPriority()" on this current thread object.

Current thread name: CB-Thread
Current th name: main
Current th id: 1
Current thread id: 15
Current thread priority: 5
Current th priority: 5

Thread.sleep()

It is always the current thread that is put to sleep, although the thread might not sleep for the required time (or even at all).

While sleeping, the thread still owns the synchronization locks it has acquired.

When Thread is sleeping and is interrupted, the method throws an "InterruptedException" immediately and doesn't wait until the sleeping time finishes.

The major difference between "wait()" from "java.lang.Object" and "sleep()" from "java.lang.Thread" is that wait() method releases the acquired monitor when the thread is waiting, while Thread.sleep() method keeps the lock or monitor even if the thread is waiting.

Current th: main
Current th: main
Current thread: CB-Thread
Current th: main
Current thread: CB-Thread

Thread.setUncaughtExceptionHandler()

The java.lang.Thread.setUncaughtExceptionHandler() sets the handler to be invoked when this thread abruptly terminates due to an uncaught exception.

Current thread: Th Thread
A critical exception happened in thread: Th Thread
Index
Modern Java - What’s new in Java 9 to Java 17

32 min

What is differences between JDK, JVM and JRE ?

2 min

What is ClassLoader in Java ?

2 min

Object Oriented Programming (OOPs) Concept

17 min

Concurrency in Java: Creating and Starting a Thread

12 min

Concurrency in Java: Interrupting and Joining Threads

5 min

Concurrency in Java: Race condition, critical section, and atomic operations

13 min

Concurrency in Java: Reentrant, Read/Write and Stamped Locks

11 min

Concurrency in Java: "synchronized" and "volatile" keywords

10 min

Concurrency in Java: using wait(), notify() and notifyAll()

6 min

Concurrency in Java: What is "Semaphore" and its use?

2 min

Concurrency in Java: CompletableFuture and its use

18 min

Concurrency in Java: Producer-consumer problem using BlockingQueue

2 min

Concurrency in Java: Producer-Consumer Problem

2 min

Concurrency in Java: Thread pools, ExecutorService & Future

14 min

Java 8 Lambdas, Functional Interface & "static" and "default" methods

28 min

Method Reference in Java (Instance, Static, and Constructor Reference)

9 min

What’s new in Java 21: A Tour of its Most Exciting Features

14 min

Java Memory Leaks & Heap Dumps (Capturing & Analysis)

9 min

Memory footprint of the JVM (Heap & Non-Heap Memory)

15 min