Multithreading in Java refers to the use of many threads and is one of Java's most essential capabilities.
As the name implies, it refers to a CPU's capacity to run several threads independently at the same time while sharing process resources.
Its primary goal is to allow multiple threads to run at the same time in order to maximise CPU utilisation.
It's a Java feature that allows you to split a programme into two or more threads to help it run faster and more efficiently.
Multithreading in Core Java(J2SE) is a key topic from the perspective of an interview.
It can help you become a Java Developer, a Java Testing Engineer, a Java Architect, a Lead Analyst, a Java Consultant, and, most importantly, a great Java programmer by giving you the confidence to dive into J2EE programming, which stands for Java to Enterprise Edition or, in layman terms, preparing you to work directly in a corporate domain workflow.
Java developers in India earn between 300,000 and 25,000,000 rupees per year, depending on their IQ level.
Two of the most common chapters in the Java programming language are multithreading and synchronisation.
Interview questions about multithreading are common in the game development industry.
Let's start with the fundamental interview questions on Multithreading in Java.
Multithreading means multiple threads and is considered one of the most important features of Java. As the name suggests, it is the ability of a CPU to execute multiple threads independently at the same time but share the process resources simultaneously. Its main purpose is to provide simultaneous execution of multiple threads to utilize the CPU time as much as possible. It is a Java feature where one can subdivide the specific program into two or more threads to make the execution of the program fast and easy
Multithreading is used to achieve multitasking.
It takes up less memory and produces quick, accurate results.
Its main advantages are as follows:
🚀
All threads share the same address space.
🚀
The thread is fine and thin.
🚀
The cost of communication between processes is very low.
Multithreading is used to achieve multitasking.
Process-based multitasking refers to the execution of many tasks at the same time, each of which is a different independent process.
Multitasking based on processes.
Running the Java IDE and TextEdit at the same time is an example.
Thread-based multitasking refers to running multiple tasks at the same time, with each task being a different, independent portion of the same software called a thread.
Threads, for example, are used by JUnits to run test cases in parallel.
Procedure-based multitasking is thus defined as a larger scenario handling process in which threads handle the details.
Thread-based multitasking is preferable to process multitasking because processes are heavyweight and require their own address space in memory, whereas threads are relatively light-weight processes that cooperatively share the same address space as heavyweight processes.
Because inter-process communication is costly and constrained, switching is a secondary consideration.
Context moving from one process to another is costly, whereas inter-thread communication is inexpensive, and context switching between threads is less costly.
Threads, as observed, are light processes within processes. Threads can be created in Java using either the Thread class or the Runnable interface. Because each thread operates in a different stack frame, it follows a separate route of execution. Multiple threads can exist in a process. Threads are process parts that simply allow a programme to run efficiently alongside other parts or threads of the process at the same time. Threads allow you to do complex tasks in the simplest way possible. It is said to be the simplest approach to take advantage of a machine's numerous CPUs. They share a same address space but are otherwise unrelated.
Multithreading has a number of advantages, which are listed below:
🚀
Allow the software to run indefinitely, even if a portion of it is disabled.
🚀
When compared to standard parallel applications that use several processes, performance is improved.
🚀
Allows you to develop efficient applications that make the most of your computer's resources.
🚀 Enhances the responsiveness of complex programmes or applications.
🚀
Increase the use of CPU resources while lowering maintenance expenses.
🚀
Time and parallelism tasks are saved.
🚀
Because threads are autonomous, an exception in one thread will not affect other threads.
🚀
It uses fewer resources than running numerous processes at the same time.
🚀Inter-thread communication is the process of communication between synchronised threads.
🚀Thread polling is avoided in Java by using inter-thread communication.
🚀The thread is paused in its crucial portion, and another thread is permitted to enter (or lock) the critical area for execution.
🚀The wait(), notify(), and notifyAll() methods can all be used to get it.
The Object class in Java provides the wait() function.
In Java, this method is used to communicate amongst threads.
The java.lang.Object.wait() method is used to halt the current thread and wait for another thread to call the notify() or notifyAll() method before continuing.
The following is the syntax for it.
public final void wait()
If we don't call the wait function, the java.lang.IllegalMonitorStateException exception will be thrown. Furthermore, with notify() and notifyAll, we require the wait() method for inter-thread communication (). As a result, for proper and correct communication, it must be present in the synchronised block.
During its existence, a thread can be in one of the following states:
🚀
A Thread class object is created using the new operator in this stage, but the thread is not alive.
The thread does not begin until the start() procedure is called.
🚀
After using the start() method, the thread is in this state and ready to run.
The thread scheduler, on the other hand, has not yet chosen the thread.
🚀
The thread scheduler selects the thread from the ready state, and it is now in the running state.
🚀
Waiting/Blocked: A thread is not running but is still alive in this state, or it is waiting for another thread to finish.
🚀
When the run() method returns, a thread is in a terminated or dead state.
Preemptive scheduling means that the highest priority task runs until it enters the waiting or dead states, or until a higher priority task appears. A job that uses time slicing performs for a predetermined amount of time before returning to the pool of ready tasks. Based on priority and other parameters, the scheduler then selects which task should be executed next.
The state of the process (or thread) is saved during context switching so that it can be restored and execution can resume from the same point later. Multiple processes can share the same CPU thanks to context switching.
Thread: It simply refers to the tiniest components of a process.
It has the ability to run several parts of the programme (referred to as threads) at the same time.
Process: It simply refers to a running programme, often known as an active programme.
PCBs can be used to manage a process (Process Control Block).
Thread | Process |
---|---|
It's a subset of a process's subunit. | It's a multi-threaded software that's now running. |
They communicate data and information amongst themselves. | They don't share information with one another. |
To avoid unforeseen events or issues, thread synchronisation is required. | Each process does not require synchronisation. |
Threads are elements of a process, therefore they are interdependent, but each thread runs on its own. | Processes are separate from one another. |
Multiple threaded processes consume fewer resources. | Threadless processes consume more resources. |
It takes less time to create, terminate, and switch contexts. | It takes more time to create, terminate, and switch contexts. |
Because threads share the same memory address as the process they belong to, inter-thread communication is faster, less expensive, simple, and efficient. | Because each process has its own memory area or address, inter-process communication is slower, more expensive, and more complicated. |
These are simpler to make, lighter, and require less overhead. | These are more complex to make, heavier, and have a higher overhead. |
Class lock : Each and every class in Java has its own lock, which is referred to as a class level lock. The keyword'static synchronised' is used to create these locks, which can be used to make static data thread-safe. It's typically utilised when numerous threads aren't allowed to enter a synchronised block.
public class Example1 { public void method1(){ synchronized (Example1.class) { //statements } } }
Object Lock: Each object in Java has its own lock, which is referred to as an object-level lock. The keyword'synchronized' is used to create these locks, which can be used to safeguard non-static data. It's commonly used to synchronise a non-static function or block such that only the thread can execute the code block on a specific instance of the class.
public class Example2 { public void method2(){ synchronized (this) { //statements } } }
In Java, there are two different types of threads:
🚀
Daemon thread
🚀User thread
Java programmers build user threads, such as the Main thread.
Because the'main' thread is non-daemon, all threads launched inside the main() procedure are by default non-daemon threads.
A daemon thread is a low-priority thread that performs background operations such as trash collection.
They don't stop daemon threads from quitting when all user threads have completed their tasks.
When all non-daemon threads have completed their execution, the JVM stops itself.
JVM does not care if a thread is running or not; if a running daemon thread is found, JVM terminates the thread and then shuts down.
As previously stated, when the JVM starts, it generates a main thread over which the application is run unless the user provides a second thread.
The first thing the "Main" thread does is look for the 'public static void main(String [] args)' method, which serves as the program's entry point.
The "Main" thread functions as a parent thread for all other threads created in main.
User threads can be implemented in one of two ways:
🚀
By extending java.lang, you can use the Thread class.
🚀
By implementing the Runnable Interface.
User Thread (Non-Daemon Thread): User threads in Java have a distinct life cycle that is independent of the life of any other thread.
Before terminating a user thread, the JVM (Java Virtual Machine) waits for it to complete its tasks.
JVM kills the entire application, including associated daemon threads, when user threads are completed.
Daemon Thread: Daemon threads are a type of service provider in Java that provides services and support to user threads.
In the thread class, there are two methods for daemon threads: setDaemon() and isDaemon() ().
User Thread | Daemon Thread |
---|---|
Before terminating the JVM, it waits for user threads to complete their duties. | Before terminating daemon threads, JVM does not wait for them to complete their responsibilities. |
The user usually creates these threads to perform multiple tasks at the same time. | JVM is usually the one who creates these threads. |
They are utilised for an application's important activities or core work. | They aren't utilised for vital duties, but rather to assist with others. |
These threads are known as high-priority jobs, and as a result, they must run in the foreground. | These threads are known as low priority threads and are used to assist background processes such as garbage collection, releasing memory for useless objects, and so on. |
We may name a thread by replacing the default naming, which was 'Thread-0,' 'Thread-1,' and so on, with a function called setName().
thread_class_object.setName("Name of the thread");
Priorities in threads is a concept in which each thread has a priority, or to put it another way, every object has a priority here, which is represented by numbers ranging from 1 to 10.
🚀As expected, the default priority is set to 5.
🚀The minimum priority has been set to zero.
🚀The maximum priority has been set at ten.
It contains three constants, which are as follows:
🚀NORM PRIORITY is a public static int.
🚀MIN PRIORITY is a public static int.
🚀MAX PRIORITY is a public static int.
If we include threads in operating systems, we can see that the process scheduling algorithms in operating systems are based on the same idea as incorporating threads in Gantt charts. Below is a list of the most popular, which summarises all of them and is used in software development.
🚀 First In, First Out
🚀: Last In, First Out
🚀 Scheduling by Robin
If one considers the concept of deadlock in operating systems with threads, one may see how the switching is computed internally if one simply has a high-level view of them.
Thread scheduling entails two types of boundary scheduling:
🚀
The application developer schedules user-level threads (ULT) to kernel-level threads (KLT) using a lightweight process (LWP).
🚀
The system scheduler schedules kernel-level threads to fulfil many distinct OS operations.
🚀If many threads are waiting to execute, the "ThreadScheduler," which is part of the JVM, determines thread execution, resulting in unexpected output order execution.
Note:
The assurance of order in multithreading is quite low, as we can predict possible outputs but not exactly one.
Also, simply by introducing the keyword'synchronized,' we can see how synchronisation, when combined with multithreading, affects our desired result.
A daemon thread is a low-priority thread that performs operations such as trash collection in the background. It does have a few unique characteristics, which are stated below:
They won't be able to stop the JVM from quitting once all of the user threads have completed their tasks.
When all user threads have completed their execution, the JVM terminates.
If JVM discovers a running daemon thread, it terminates the thread before shutting down. The JVM is unconcerned about whether the Daemon thread is active or not.
It's a very low-priority thread.
The fundamental distinction between a user thread and a daemon thread is that the JVM does not wait for the daemon thread before departing, whereas the user thread does.
It's done with the help of the setDaemon() and isDaemon() methods from the 'Thread class' (). The setDaemon() method, for example, transforms a user thread to a daemon thread and vice versa. This method can only be used before the thread is started using start () If the method else is called after the thread has been started, it will throw an IllegalThreadStateException. Following that, the isDaemon() method is utilised, which returns a boolean true if the thread is a daemon and false if it is not.
The main purpose of the start() method is to register the thread with the thread scheduler so that the thread scheduler knows what the child thread should do, when it should do it, and how it should be scheduled. The second duty is to call the threads' appropriate run() function.
First, both procedures are applied to the thread as a whole. So, if we use threadT1.start() to start a new thread, this method will seek for the run() method. While in the case of theadT1.run(), the "Main" thread will most likely execute the usual method without creating a separate thread.
Note that if we substitute start() with run(), the entire programme is carried out by the'main' thread.
First, both procedures are applied to the thread as a whole. So, if we use threadT1.start() to start a new thread, this method will seek for the run() method. While in the case of theadT1.run(), the "Main" thread will most likely execute the usual method without creating a separate thread.
Note that if we substitute start() with run(), the entire programme is carried out by the'main' thread. Yes, you can overload run() by adding parameters to it and making sure that @override is commented out of the run() method.
It should be as good as a thread with no arguments, therefore practise overloading by commenting on the call out for the overloaded run() method. So, if the output is the same or not if we haven't overloaded it, we must acknowledge the same.
If we've overloaded the run() function, we'll notice that output is always the main method, as evidenced by the stack call in the screenshot above.
Because we haven't overridden the run() function, when we debug the code as shown in the link below, run() is executed as soon as the start() method is invoked again.
For further information on how the run() method is overloaded, see the Implementation section.
Conclusion: We can overload the run() function, but the start() method will only call the run() method with no arguments. As a result, it will be of no use to us and is deemed poor practise.
The compiler will simply execute the Thread class's run() method, taking in mind that the Thread class's run() method must have an empty implementation. As a result, no output matching to the thread is produced.
Even if we override the start() method in the custom class, the Thread class will not carry out any initializations for us. The run() method isn't invoked, and a new thread isn't generated either.
Note: We cannot resume the same thread since the java.lang package will throw an IllegalThreadStateException. Furthermore, using the'super.start()' technique will not allow us to achieve this indirectly.
There are two ways to make the Thread.
🚀Extending the Thread class is a good way to start.
🚀Implementing the Runnable interface is a great way to get started.
However, the following are the main distinctions between the two methods:
We can't extend any other classes by extending the Thread class since Java doesn't allow multiple inheritance while implementing the Runnable interface; however, we can extend other base classes (if required).
Each thread extends the Thread class, which produces and associates with a unique object while implementing the Runnable interface; numerous threads share the same object.
The Thread class has several built-in methods such as getPriority(), isAlive(), and others, however the Runnable interface only has one function, which is run ().
The wait() method waits for a thread to die before joining it. In other words, it causes the threads that are presently running to pause until the thread it joins completes its task. In the Thread class, the Join method is overloaded in the following ways.
🚀
InterruptedException is thrown by public void join().
🚀InterruptedException is thrown by public void join(long milliseconds).
In Java, the sleep() function is used to block a thread for a specified amount of time, i.e., it pauses the thread's execution.
There are two options for doing so.
Syntax:
The public static void sleep throws an InterruptedException (long milliseconds).
The public static void sleep throws an InterruptedException (long milliseconds, int nanos).
The sleep() method is currently being used.
When we execute the sleep() method, the current thread's execution is paused for the specified period of time and another thread is given priority (if available).
Furthermore, after the waiting period expires, the previous thread switches from waiting to runnable and enters the running state, and the process repeats until the execution is completed.
No, we can't restart the thread because it's in the Dead state after it's initiated and executed. As a result, attempting to start a thread twice will result in a "java.lang.IllegalThreadStateException" runtimeException. Consider the following illustration.
public class Multi_thread extends Thread { public void fun() { try { System.out.println("Hello Mojo's........"); } catch(Exception e) { } } public static void main (String[] args) { Multithread1 mul1= new Multi_thread(); mul1.start(); } }
Yes, explicitly invoking the run() method is allowed, but it will not work as a thread, but rather as a regular object. Between the threads, there will be no context switching. When we use the start() function, it calls the run() method internally, which produces a new stack for a thread, whereas using run() directly does not.
wait(): As the name implies, this is a non-static method that causes the current thread to wait and sleep until another thread calls the object's monitor's notify () or notifyAll() method (lock). It merely unlocks the lock and is typically used for thread-to-thread communication. It's in the object class, and it should only be used in a synchronised context.
synchronized(temp) { temp.wait(); Current Thread released the lock }
sleep(): As the name implies, this is a static method that pauses or stops the current thread's execution for a set amount of time. It doesn't relinquish the lock while waiting, and it's mainly used to add a pause to the execution process. There's no need to call it from a synchronised context because it's declared in the thread class.
synchronized(temp) { temp.sleep(10); //After 10 milliseconds, or after we invoke the interrupt() method, the current thread will wake up. }
wait() | sleep() |
---|---|
In the Object class, the wait() method is defined. | The Thread class defines the sleep() method. |
The lock is released using the wait() method. | The lock is not released by the sleep() procedure. |
notify(): It sends a notification and only wakes up one thread, rather than several threads waiting on the object's monitor.
notifyAll(): This method sends out notifications and wakes up all threads, allowing them to compete for the object's monitor rather than a single thread.
Every object, we know, has a monitor that allows the thread to keep a lock on it. However, there are no monitors in the thread class. The wait() function on an object is commonly used by a thread to wait for the object's monitor (lock), and the notify() or notifyAll() method is used to notify other threads that are waiting for the same lock. As a result, these three methods are only called on objects, allowing all threads formed on that object to communicate with one another.
The shutdown hook is an implicit thread that is called before the JVM shuts down. As a result, when the JVM shuts down normally or abruptly, we can use it to clean up the resource or save the state. We can use the following way to create a shutdown hook:
public void temp(Thread hook){ } Runtime r=Runtime.getRuntime(); r.temp(new fun());
The following are some key factors to remember about shutdown hooks:
🚀Shutdown hooks have been set up, but they can only be used once the JVM has been shut down.
🚀Shutdown hooks are more dependable than finalizer() because shutdown hooks have a lower possibility of not running.
🚀The shutdown hook can be disabled by calling the Runtime class's halt(int) function.
We should interrupt a thread if we want it to wake up from its sleep or wait state. Interrupting a thread is done by invoking interrupt(), which throws the InterruptedException.
Synchronization is the ability to manage several threads' access to a common resource.
It's used for:
🚀In order to avoid thread interference.
🚀To avoid a consistency issue.
When numerous threads attempt to perform the same operation, an incorrect outcome is possible; hence, Java employs the synchronisation mechanism, which permits only one thread to be run at a time.
There are three approaches to accomplish synchronisation:
🚀by static synchronisation
🚀by synchronised method
🚀 by synchronised block
It's syntax is given below :
synchronized( reference object expression) { //Statements }
The Synchronized block can be used to synchronise any of the method's specified resources. A single thread can execute on a resource at a moment, and any other threads attempting to enter the synchronised block are prevented.
For any shared resource, a synchronised block is used to lock an object.
The synchronised block's scope is restricted to the block to which it is applied. It has a smaller scope than a technique.
Yes. By putting an object in a "synchronised" block, you can lock it. Any thread other than the one that expressly claimed it is unable to access the locked object.
By running the code on cmd and collecting the Thread Dump, we can discover a deadlock issue; if the code contains any deadlocks, a notification will be presented on cmd.
In Java, there are a few ways to avoid a deadlock:
🚀
Avoid Nested Lock: Nested lock is a typical cause of deadlock because it occurs when we issue locks to several threads at the same time. We should only provide one lock to one thread at a time.
🚀
Avoid locks that aren't necessary: we must avoid locks that aren't necessary.
🚀Using thread join: Thread join allows us to wait for a thread till another thread completes its execution, allowing us to prevent deadlock by making the most of the join method.
A Thread Scheduler, which is part of the JVM, is used to supervise threads when they are created in Java.
The thread scheduler's sole responsibility is to determine which threads should run.
Preemptive and time slicing scheduling strategies are used by the thread scheduler.
The Java thread scheduler can additionally tell you which of the following is true for a thread:
🚀It determines the thread's priority.
🚀It determines how long a thread will be idle.
🚀It examines the thread's nature.
Yes, each thread in multithreaded programming has its own or separate stack space in memory, making each thread independent of the others.
A method or class object is thread-safe if it can be used by multiple threads at the same time without causing a race condition. Thread safety is a technique for making a multithreaded software safe to use. It is possible to do so in the following ways:
🚀Synchronization
🚀Using the term "volatile"
🚀Using a lock-based system
🚀Atomic wrapper classes are used.
A race condition is an issue that happens in multithreaded programming when many threads run at the same time and access a shared resource. The Race condition can be avoided with adequate synchronisation.
The volatile keyword is used in multithreaded programming to achieve thread safety, as changes in one volatile variable are visible to all other threads, allowing only one thread to use a variable at a time.
🚀
A Java Thread Pool is a collection of worker threads that are waiting for a job to be assigned to them.
🚀The service provider supervises threads in the thread pool, pulling one thread from the pool and assigning it a job.
🚀Thread returned to the thread pool after completing the assigned task.
🚀The thread pool's size is determined by the total number of threads held in reserve for execution.
The thread pool has the following benefits:
🚀Performance can be improved by using a thread pool.
🚀System stability can be improved by using a thread pool.
The java.util.Concurrent package's classes and interfaces can be used to create a concurrency API.
The java.util.Concurrent package contains the following classes and interfaces.
🚀Executor
🚀ScheduledExecutorService
🚀FarkJoinPool
🚀Future
🚀ExecutorService
🚀TimeUnit(Enum)
🚀CyclicBarrier
🚀CountDownLatch
🚀ThreadFactory
🚀Semaphore
🚀BlockingQueue
🚀Locks
🚀DelayQueue
🚀Phaser
The Callable and Runnable interfaces are both used by classes that want to run several threads at the same time.
However, there are two significant distinctions between the two:
🚀The Callable interface can return a result, whereas the Runnable <V> interface can't.
🚀A checked exception can be thrown by the Callable interface, but not by the Runnable <V> interface.
🚀Before Java 5, the Callable interface could not be used, although the Runnable interface could.
🚀The atomic action is a task operation that can be completed in a single unit without interfering with other tasks.
🚀The Atomic action cannot be paused in the middle of a task.
🚀Once begun, it will only stop when the work is completed.
🚀An atomic action is not possible with an increment operator like a++.
🚀Except for long and double, all reads and writes for primitive variables are atomic operations.
🚀The atomic operation is used for all reads and writes for the volatile variable (including long and double).
🚀The java.util.Concurrent package contains the Atomic methods.
The locks in the java.util.concurrent.locks package.
The synchronisation mechanism is the Lock interface.
It functions in a similar way to the synchronised block.
The lock and synchronised blocks differ in a few ways, which are listed below.
🚀The lock interface ensures that the waiting thread will be granted access in the correct order, but the synchronised block does not.
🚀If the lock is not granted, the lock interface gives a timeout option, whereas the synchronised block does not.
🚀Lock interface methods, such as Lock() and Unlock(), can be called from several methods, whereas a single synchronised block must be completely contained in a single method.
Synchronous programming: In the Synchronous programming model, a thread is allocated to finish a job and therefore began working on it; it is only available for additional tasks once the assigned task has been completed.
Asynchronous Programming: In asynchronous programming, one job can be completed by numerous threads, allowing the individual threads to be used to their full potential.
Java Callable Interface: The package java.util.concurrent offered a callable interface in Java5. It's similar to the Runnable interface, except that it can return a result and throw an exception. It also includes a run() method for thread execution. Because Generic is used, Java Callable can return any object.
The result of a concurrent process is returned using the Java Future interface. The java.util.concurrent.Future object is returned by the Callable interface.
For implementation, Java Future provides the methods listed below.
🚀
cancel() :It is used to cancel the execution of the allocated task. cancel(boolean mayInterruptIfRunning): It is used to cancel the execution of the assigned task.
🚀get(): If the execution hasn't finished yet, it waits for the timer to expire before retrieving the result.
🚀isCancelled(): This function returns a Boolean value of true if the job was cancelled before it was completed.
🚀isDone(): If the job is completed successfully, it returns true; otherwise, it returns false.
Both ExecutorServcie and ScheduledExecutorService are java.util interfaces. Concurrent package, but scheduledExecutorService adds some extra methods for running Runnable and Callable activities with a delay or at a set interval.
The FutureTask class in Java implements the Future interface as a basis. The result can only be accessed if one task is done, and the get method will be blocked if the computation is not completed. Once an execution has been finished, it cannot be re-started or cancelled.
public class temp<V> extends Object implements RunnableFuture<V>
Both CyclicBarrier and CountDownLatch are essential for multithreaded programming management. However, there are some distinctions between them, as seen below:
CyclicBarrier is a tool that uses an algorithm to synchronise thread processing. It allows a group of threads to wait for each other until they reach a common execution point or barrier point, after which they can proceed with their execution. Even if the barrier is broken by setting it, the same CyclicBarrier can be used again.
CountDownLatch: This is a tool that allows main threads to wait until other threads have completed all mandatory operations. In simple terms, it ensures that a thread waits for another thread's execution to finish before beginning its own. Once the count reaches 0, the same CountDownLatch cannot be used again.
As the name implies, inter-thread communication is a method or technique for several threads to communicate with one another. It may be retrieved using the wait(), notify(), and notifyAll() methods in Java, and is notably useful for avoiding thread polling.
Thread Scheduler: This is a JVM component that determines which thread will execute next if there are numerous threads waiting to be executed.
The thread scheduler chooses the next run to execute by looking at the priority assigned to each thread that is READY.
It primarily employs two strategies to schedule the threads: Preemptive Scheduling and Time Slicing Scheduling.
Time Slicing: This technique is particularly useful for dividing CPU time and allocating it to active threads.
Each thread will be given a predetermined amount of time to execute.
When the timer runs out, a thread must wait until other threads in a round-robin method have a chance to use their time.
Busy Spinning, also known as Busy-waiting, is a technique in which one thread waits for a condition to happen, without executing wait or sleep methods and releasing the CPU. In this case, a thread can be paused by making it run an empty loop for a set amount of time, and it does not give CPY control. As a result, it is utilised to conserve CPU caches and save money on cache rebuilding.
ConcurrentHashMap is a Java 1.5 feature that allows you to store data in several buckets at the same time. It permits concurrent read and write operations to the map, as the name implies. It merely locks a piece of the map while iterating to offer thread safety, allowing other readers to access the map without having to wait for iteration to finish.
Hashtable: A hash table is a thread-safe legacy class that was introduced in earlier versions of Java to hold key or value pairs. Unlike ConcurrentHashMap, it does not support lock-free reading. It just locks the entire map while doing iteration
Both ConcurrentHashMap and Hashtable are thread-safe, however ConcurrentHashMap, unlike Hashtable, eliminates read locks and improves speed.
Unlike Hashtable, ConcurrentHashMap also allows for lock-free reads.
As a result, ConcurrentHashMap outperforms Hashtable, particularly when the number of readers outnumbers the number of writers.
Thread priority simply means that high-priority threads will be executed first, followed by low-priority threads. Although the priority can be specified, it is not required that the highest priority thread be executed before the lower-priority thread. Thread scheduler assigns processor to thread on the basis of thread priority. The priority scale ranges from 1 to 10, with 1 being the lowest and 10 being the highest.
A database lock is a method that prevents two or more database users from updating the same piece of data at the same time.
When a lock is acquired by a single database user or session, no other database user or session can edit the data until the lock is released.
🚀 Shared Lock :A shared lock is necessary for reading a data item, and in a shared lock, multiple transactions can hold a lock on the same data item.
A shared lock allows many transactions to read the data items.
🚀Exclusive Lock :A lock on any transaction that is about to perform a write operation is known as an exclusive lock.This form of lock prevents any database inconsistency by allowing just one transaction at a time.
Both interfaces are commonly used to encapsulate tasks that must be completed by a different thread.
However, there are some distinctions between them, as listed below:
Running Interface: This interface has been present in Java since the beginning.
It's just a way to run code on multiple threads at the same time.
The Callable Interface is a brand-new interface that was introduced as part of the concurrency package.
It addresses runnable interface limitations as well as several key modifications such as generics, enums, static imports, variable argument methods, and so on.
It makes use of generics to provide the object's return type.
Runnable Interface | Callable Interface |
---|---|
Overriding the run() method is required to use this interface. | Overriding the call() method is required to use this interface. |
It creates a task with the run() method. | It creates a task using the call() method. |
It's just a part of Java.lang. | It's just a part of the java.util.concurrent package. |
It first appeared in JDK 1.0. | It was first introduced in JDK 5.0, hence it can't be used prior to Java 5. |
It doesn't produce any output, thus it can't throw a checked exception. | Because it returns a result, it can throw an exception. |
It is not possible to send it to the invokeAll method. | It's possible to pass it to the invokeAll method. |
start(): The start() method is used to initiate or start the execution of a newly formed thread.
When the start() method is called, a new thread is started, which runs the task stored in the run() method.
The start() method can only be used once.
run(): To put it another way, the run() method is used to start or restart the execution of the same thread.
In contrast to the start() method, the run() method does not generate a new thread.
The current thread executes this function.
The run() method can be used many times.
Garbage collection is essentially an automated memory management technique. It employs a number of GC algorithms, the most prominent of which being Mark and Sweep. Marking, deletion, and compaction/copying are the three phases of the procedure. To put it another way, a garbage collector looks for items that are no longer needed by the application and deletes or removes them to free up memory space.
In multithreaded programming, a volatile variable is a keyword that ensures and addresses the visibility of variable changes. This keyword does not work with classes or methods, but it does work with variables. Its sole purpose is to ensure thread safety. If a variable is marked as volatile, all threads can read its value directly from main memory rather than the CPU cache, allowing each thread to acquire the variable's most recent value.
Wait(), notify(), and notifyAll are three methods that threads can use to communicate ()
Finalize() is an Object class function that is used to perform cleanup activities on unmanaged resources right before garbage collection. It is not intended to be referred to as a standard procedure. The object is automatically destroyed after the finalise() method has completed its execution.
Synchronized Method: When a thread enters a synchronised method, it acquires a lock on the object and releases it either normally or by throwing an exception when it exits the method.
Until the current thread completes its execution and releases the lock, no other thread can use the entire method.
It's useful when you want to lock down the complete functionality of a method.
Synchronized Block: The thread acquires a lock on the object within parentheses after the synchronised keyword in this procedure, and releases the lock when they exit the block.
Until and unless the synchronised block exists, no other thread can gain a lock on the locked object.
It's useful when you want other threads to have access to different portions of the programme.
Synchronized blocks should be preferred more because they improve a program's performance.
It merely locks a certain area of the programme (the important section) rather than the full method, resulting in reduced conflict.
Thread starvation is a situation or state in which a thread will be unable to access shared resources on a regular basis and so will be unable to advance. This is due to other threads having a high priority and occupying the resources for an excessive amount of time. This is most common with low-priority threads that do not receive enough CPU to continue their execution.
Livelock is a concurrency problem that is similar to deadlock. In this situation, the condition of the threads shifts from one to the next without progressing. Threads are not blocked, but their execution is halted owing to resource shortages.
The ThreadSafeBlockingQueue class represents a thread-safe queue. The producer thread uses the put() method to add resources/elements to the queue until it is full, and the consumer thread uses the take() method to remove resources from the queue until it is empty. However, if a thread attempts to dequeue from an empty queue, it will be blocked until another thread inserts an item into the queue, or if a thread attempts to insert an item into an already full queue, it will be blocked until other threads remove an item from the queue.
ThreadLocal variables are unique variables that the Java ThreadLocal class creates and provides. Only the same thread is permitted to read and write these variables. Because two threads cannot view each other's ThreadLocal variable, there will be no race condition and the code will be thread-safe even if they execute the identical code.
Semaphore is a thread synchronisation mechanism that is commonly used to regulate and manage access to a shared resource via counters. It just sets the thread's limit. The semaphore class is found in the java.util.concurrent package and can be used to communicate signals across threads in order to prevent missed signals or to protect key portions. It's also useful for creating resource pools and bounded collections.
ThreadGroup is a class that allows you to combine numerous thread groups into a single object.
Except for the first thread, this set of threads is represented by three structures, each of which has a parent.
Thread groups can also contain thread groups.
A thread can only access information about its own thread group, not information from other thread groups.
The only functionality that did not operate without a thread group in the previous version of Java was uncaughtException ( Thread t, Throwable e).
Thread.setUncaughtExceptionHandler is now available in Java 5 versions (UncaughtExceptionHandler).
As a result, even that now works without thread groups, and thread groups are no longer required.
The ExecutorService interface is a sub-interface of the Executor interface with certain additional methods or capabilities that aid in thread management and control. It allows us to run tasks on threads asynchronously.
If we don't override the run() method, nothing will happen. There will be no errors displayed by the compiler. It will call the thread class's run() method, but there will be no output because the run() method has an empty implementation.
class TempThread extends Thread { //don't override run() method } public class fun1 { public static void main(String[] args) { System.out.println("Hey Main Started."); TempThread thread1=new x(); x.start(); System.out.println("Hey Main Ended."); } }
Output :
Hey Main Started. Hey Main Ended.
The Lock interface was first introduced in Java 1.5 and is primarily used as a synchronisation mechanism for blocking operations.
The following are some of the benefits of using the Lock interface instead of the Synchronization block:
🚀
The Lock interface's Lock() and Unlock() functions can be used in a variety of ways.
🚀Because a synchronised block is totally confined in a single method, the main advantage of a lock interface over a synchronised block is this.
🚀
Unlike the synchronisation block, the lock interface is more flexible and ensures that the longest waiting thread has a fair chance to execute.
No, that is not possible. To start a new thread, you must use the start method; otherwise, the run function will not start a new thread. It will instead run in the current thread.
It is, without a doubt, possible. Because each thread in multithreaded programming has its own separate stack space in memory, each thread is independent rather than dependent on the others.
Multithreading is an important aspect of Java and modern software development in general. It is quite beneficial in terms of making the software more effective while also reducing the amount of storage resources used. In this post, we've covered key interview questions on multithreading, as well as answers that have been asked frequently in interviews and can help you ace your interviews.