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