6.What are wait(), notify(), and notifyAll() in Java?
Wait(), notify() and notifyAll() are the ways of inter thread communication in Java. These allow threads to be coordinated based upon certain conditions getting met.
wait(): Makes the current thread release the lock on the object and go into a waiting state until it is notified by another thread.
notify(): Resumes one of the threads waiting on the monitor of the object. If there are multiple threads waiting, the system arbitrarily picks up one.
notifyAll(): Resumes all the threads waiting on the monitor of the object so that they compete to acquire the lock.
These methods are always called within a synchronized block or method, since they are dependent on the monitor of the synchronized object.
Example:
synchronized (lock) {
while (!conditionMet) {
lock.wait(); /* The thread waits until notified*/
}
/* Code to execute after the condition is met*/
}
synchronized (lock) {
lock.notify(); /* Notify one waiting thread*/
}
These methods are very helpful when situations like the producer consumer problems arise in which threads need to coordinate their actions based on shared conditions.
7.What are Deadlock, Livelock and Starvation in Java and how can they be prevented?
Deadlock
A deadlock is when two or more threads are stuck in an infinite loop each waiting for the other to free up a resource it needs. Suppose Thread A locks Resource 1 and needs Resource 2 while Thread B locks Resource 2 and needs Resource 1. In this case neither thread can continue. It creates a circular waiting loop that freezes progress.
Avoiding Deadlocks:
Maintain a Consistent Lock Order, locks should be acquired in the same order across all threads to prevent the problem of circular dependencies.
Implement Timeouts: A timeout is used to limit how long a thread waits for a lock. If the lock is not acquired in this time, the thread can release any held locks and try again later.
Keep Lock Usage Minimal: Minimize the time a lock is held and avoid holding multiple locks simultaneously unless absolutely necessary.
Livelock
Livelock is almost like two people stepping aside time and again trying to let the other pass they are both active but never make any progress. In Java, livelock is said to happen when threads are busy responding to each other's action so heavily that no meaningful execution is possible.
Avoiding Livelocks:
Introducing Random Delays: Introduce short random pauses between retry attempts to reduce the likelihood of continuous interference.
It should avoid any overreaction to changes in other threads without making progress.
Starvation
Starvation is found when some thread is kept waiting indefinitely because of resources are monopolized by other threads. This often occurs in the cases when one scheduling mechanism favors the execution of some threads against others and leaves the lower priority threads waiting forever.
Preventing Starvation:
Use Fair Locks : Take the implementations of fair synchronization, such as ReentrantLock with fairness on to ensure that threads are served in the order of their requests for resources.
Avoid High Thread Priorities: Unless required the worst, one should not set extremely high priorities to a few threads.
8.What is Mutual Exclusion in Java and How Does It Work?
It ensures that only one thread can have access to a common resource or critical section at any instant. That's quite essential to prevent data inconsistency and race conditions in multi threaded environments.
How It Works:
There are two main methods in Java that are used for providing mutual exclusion:
Synchronized Methods/Blocks: The keyword synchronized is a straightforward method of locking the critical sections. Once a thread is inside a synchronized block other threads need to wait until the lock is released.
Locks (Explicit): By using java.util.concurrent.locks.ReentrantLock, you have more fine-grained control over locking including try-lock and timeout.
Example of a Synchronized Block:
public class Account {
private int balance = 500;
public void deposit(int amount) {
synchronized (this) {
balance += amount;
}
}
public void withdraw(int amount) {
synchronized (this) {
if (balance >= amount) {
balance -= amount;
}
}
}
public int getBalance() {
return balance;
}
}
In this example, synchronized ensures that only one thread can modify the balance at a time, thus avoiding conflicts.
9.How Do You Avoid Deadlock in a Multithreaded Java Application?
Avoiding deadlock requires proactive design techniques to avoid cycles of dependency. Some of the practical techniques are as follows:
1. Lock Ordering
Acquire locks in some pre-designed sequence always. For example, if there are multiple threads that need locks on two resources named Resource A and Resource B, acquire Resource A from all the threads before acquiring Resource B from them.
2. Timeout Mechanism
Use locks with timeouts to limit how long a thread waits for a resource. If the lock cannot be acquired within the timeout, the thread should abandon its operation and try again later.
Example Using ReentrantLock:
import java.util.concurrent.locks.ReentrantLock;
public class DeadlockPrevention {
private static ReentrantLock lock1 = new ReentrantLock();
private static ReentrantLock lock2 = new ReentrantLock();
public static void main(String[] args) {
Thread thread1 = new Thread(() -> attemptLock(lock1, lock2));
Thread thread2 = new Thread(() -> attemptLock(lock2, lock1));
thread1.start();
thread2.start();
}
private static void attemptLock(ReentrantLock lockA, ReentrantLock lockB) {
try {
if (lockA.tryLock() and lockB.tryLock()) {
try {
System.out.println(Thread.currentThread().getName() + " acquired both locks!");
} finally {
lockB.unlock();
lockA.unlock();
}
} else {
System.out.println(Thread.currentThread().getName() + " failed to acquire locks.");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
This code employs tryLock to avoid a deadlock as the thread acquires both locks only if they are available without blocking.
3. Deadlock Detection
Periodically inspect thread states and lock usage patterns to identify a potential deadlock situation early. Thread dump analysis tools or profilers can be applied for this.
4. High-Level Concurrency Utilities
You can make use of Java concurrency utilities like Semaphore, CountDownLatch, or CyclicBarrier. These have inherent mechanisms for dealing with dependency, thus preventing deadlock situations efficiently.
By following the above approaches, you can build strong multithreaded applications that do not deadlock but perform well under contention for resources, avoiding deadlocks, livelocks, and starvation.
10.How to Implement Thread Safety Without Using Synchronization in Java?
Thread safety does not always require explicit synchronization. Java provides built-in tools in the java.util.concurrent package, such as atomic variables and concurrent collections to handle concurrency without locking mechanisms.
Using Atomic Variables for Safe Updates
Atomic variables such as AtomicInteger, AtomicBoolean and AtomicReference, have methods that carry out thread-safe operations. All these operations are atomic; that is, they do not get interrupted at any stage; hence no two threads will interfere with each other.
AtomicExample using AtomicInteger
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicExample {
private static final AtomicInteger counter = new AtomicInteger(0);
public static void main(String[] args) {
System.out.println("Initial Value: " + counter.get());
counter.incrementAndGet(); // Safely increment
System.out.println("Updated Value: " + counter.get());
}
}
Concurrent Collections for Shared Data
Java's concurrent collections which include ConcurrentHashMap, CopyOnWriteArrayList and BlockingQueue are designed for multithreaded environments. Such collections handle internal synchronization, thus multiple threads can safely access or modify it.
Example using ConcurrentHashMap:
import java.util.concurrent.ConcurrentHashMap;
public class ConcurrentMapExample {
public static void main(String[] args) {
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
map.put("Task1", 10);
map.put("Task2", 20);
System.out.println("Value for Task1: " + map.get("Task1"));
}
}
These utilities allow you to achieve effective thread safety without the complexity of explicit synchronization.
11.What is ReentrantLock and how does it differ from synchronized in Java?
ReentrantLock is a part of the java.util.concurrent.locks package and a powerful alternative to the synchronized keyword. This class offers enhanced flexibility and control in locking beyond what can be achieved with the synchronized keyword and is ideal for complex synchronization scenarios.
Important Features of ReentrantLock:
Reentrancy: A thread holding the lock may acquire it multiple times without blocking itself.
Advanced Lock Options: Methods such as tryLock() allow for non-blocking attempts to acquire the lock and lockInterruptibly() allows threads to respond to interrupts while waiting.
Explicit Unlocking: Unlike synchronized, which automatically releases locks, ReentrantLock requires explicit unlocking in a finally block for exact control.
Differences Between ReentrantLock and synchronized:
Reentrancy: Both are reentrant but ReentrantLock provides better control with more features.
Timeout Support: Using tryLock(), ReentrantLock permits a thread to attempt to acquire a lock for a period of time.
Interrupt Handling: lockInterruptibly() enables a thread to leave a wait status if interrupted whereas synchronized does not.
Explicit Control: In ReentrantLock, explicit calls must be issued to both lock() and unlock(). That makes the control over locking logic extremely clear.
Example using ReentrantLock:
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockExample {
private static final ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
lock.lock(); // Acquire the lock
try {
System.out.println("Critical section accessed by " + Thread.currentThread().getName());
} finally {
lock.unlock(); /* Always release the lock*/
}
}
}
When you need advanced features like non-blocking lock attempts or interruptible waits, ReentrantLock is a better choice than synchronized.
12.What Is CountDownLatch and How Is It Used in Java?
CountDownLatch is a utility class for multithread synchronization. It provides a way to wait until one or more threads have completed their work. It keeps a count, which threads decrement as they complete their work. When the count reaches zero all waiting threads are released.
Use Cases of CountDownLatch
Waiting for a number of threads to complete their work before moving ahead.
Synchronize threads at some common checkpoint
How it works:
A latch is initialized by setting the latch's count to the number of tasks that need to be waited for.
Tasks call countDown() once they are done.
Tasks waiting at the latch call await() which blocks the thread until the count is zero
Here is how it works,
import java.util.concurrent.CountDownLatch;
public class CountDownLatchExample {
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(3); /* Wait for 3 tasks*/
for (int i = 1; i <= 3; i++) {
new Thread(new Worker(latch), "Task-" + i).start();
}
latch.await(); // Main thread waits for tasks to complete
System.out.println("All tasks finished. Main thread resumes.");
}
}
class Worker implements Runnable {
private final CountDownLatch latch;
Worker(CountDownLatch latch) {
this.latch = latch;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " is doing its job.");
latch.countDown(); /* Signal task completion*/
}
}
Output:
Task-1 is doing its job.
Task-2 is doing its job.
Task-3 is doing its job.
All tasks finished. Main thread resumes.
CountDownLatch is very useful in a situation where the tasks must be made aware of the synchronization of their progress or when you need the main thread to wait until all the subtasks are completed.
13.What is semaphore in Java and how does it work?
Semaphore in Java is a strong synchronization tool to manage the access of threads to some common resources. It can be thought of as a traffic signal for threads, which lets only a predetermined number of them access a resource at a given time. The allowability of the fixed number of permit systems is the mechanism used in semaphores. Every thread needs a permit to proceed and on the completion of the job it needs to release it. In case, if no permits are available a thread pauses and waits till one is released.
Types of Semaphores
Binary Semaphore: This is like a lock as it permits only one thread to progress at a time
.
Semaphore counting - It allows any fixed number of threads to use a resource provided they have got the permits number.
Example:
Semaphore in Operation
import java.util.concurrent.Semaphore;
public class SemaphoreDemo {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(2) ; /*Allows 2 permits*/
Runnable task = () ->
try {
semaphore.acquire(); /* Acquire a permit
System.out.println(Thread.currentThread().getName() + " is using the resource.");
Thread.sleep(2000); /* Simulate resource usage*/
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally{
System.out.println(Thread.currentThread().getName() + " has released the resource.");
semaphore.release(); /* Release the permit*/
}
};
/* Creating multiple threads*/
for (int i = 1; i <= 4; i++) {
new Thread(task, "Thread-" + i).start();
}
}
}
Output:
Thread-1 is using the resource.
Thread-2 is using the resource.
Thread-1 has released the resource.
Thread-3 is using the resource.
Thread-2 has released the resource.
This example demonstrates how semaphores ensure orderly access to shared resources, making your application thread-safe and efficient.
14.Difference Between wait(), notify(), and notifyAll() in Java?
What is the difference between ReentrantLock and the Lock Interface in Java?
The Lock interface in Java offers a basis for thread synchronization but ReentrantLock is an implementation of this interface. Knowing the difference between them will help you decide which to use in your application based on its complexity.
Key Differences
Scope:
The Lock interface is general and has a generic set of methods to get synchronized: lock(), unlock() and tryLock().
ReentrantLock is more enhanced and feature-rich in implementation compared to the Lock interface.
Special Features of ReentrantLock:
Lock can be designed with a fairness policy so that the threads come into the access scope in the sequence in which they were requesting.
Interruptibility of threads: An interrupted thread can interrupt waiting for acquiring the lock.
Reentrancy: A thread can re-acquire the same lock more than once without blocking.
Other Implementations:
Besides Lock interface, there are other classes such as ReadWriteLock which has a different usage.
Usage: ReentrantLock
java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockExample{
private static final Lock lock = new ReentrantLock();
public static void main(String[] args) {
Runnable task = () -> {
lock.lock(); // Acquire the lock
try {
System.out.println(Thread.currentThread().getName() + " has entered the critical section.");
Thread.sleep(1000); // Simulate work
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
System.out.println(Thread.currentThread().getName() + " is leaving the critical section.");
lock.unlock(); // Release the lock
}
};
// Start multiple threads
for (int i = 1; i <= 3; i++) {
new Thread(task, "Thread-" + i).start();
}
}
}
Output:
Thread-1 has entered the critical section.
Thread-1 is leaving the critical section.
Thread-2 has entered the critical section.
You have the ability to get fine-grained control over thread synchronization using ReentrantLock. It is flexible beyond what you can achieve using synchronized blocks.
15.What is the CyclicBarrier class in Java and how does it work?
CyclicBarrier is a synchronization utility that lets execution in phases, providing threads to coordinate with each other. Analogous to the concept of a meeting, all the involved threads reach the meeting point before moving forward. When every single thread has reached the barrier some action (if defined earlier) can be performed and then the threads move ahead to the succeeding stage. What's special is that the barrier resets automatically, so it is reusable for future synchronization points.
Why Use CyclicBarrier?
This is particularly suitable for applications where threads must coordinate periodically or at some checkpoints.
It is quite often used in parallel computing as well as in multistage algorithms.
CyclicBarrier in Real Life
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierDemo {
public static void main(String[] args) {
CyclicBarrier barrier = new CyclicBarrier(3, () ->
System.out.println("All threads have reached the barrier. Moving to the next phase.");
});
Runnable task = () -> {
try {
System.out.println(Thread.currentThread().getName() + " is performing its task.");
Thread.sleep(1000); // Simulate task
System.out.println(Thread.currentThread().getName() + " is waiting at the barrier.");
barrier.await(); // Wait for all threads
} catch (InterruptedException | BrokenBarrierException e) {
Thread.currentThread().interrupt();
}
};
// Launching multiple threads
for (int i = 1; i <= 3; i++) {
new Thread(task, "Thread-" + i).start();
}
}
}
Output:
Thread-1 is executing its task.
Thread-2 is executing its job.
Thread-3 is executing its job.
Thread-1 is waiting at the barrier.
Thread-2 is waiting at the barrier.
Thread-3 is waiting at the barrier.
All threads have reached the barrier. Proceeding to the next stage.
This is an example that shows how the CyclicBarrier ensures that the threads synchronize perfectly, making it ideal for those situations where teamwork among threads is involved. It is also reusable which makes it highly efficient for multi-phase processes
Previous Topic==> Basic Thread concepts in Java FAQ. || Next Topics==> Advanced Thread Concurrency in Java FAQ
Other Topic==>
Banking Account Management Case Study in SQL
Top SQL Interview Questions
Employee Salary Management SQL FAQ!. C FAQ
Top 25 PL/SQL Interview Questions
Joins With Group by Having
Equi Join
Joins with Subqueries
Self Join
Outer Join