How do you define and start a thread in Java?

In Java programming language, there are two ways to define and start Thread in Java.

Providing a Runnable object: Runnable interface defines a single method called run() that is meant to contain the code executed in the thread. The Runnable object is passed to the Thread constructor as shown in the below example:

public class MyRunnable implements Runnable {

    public void run() {
        System.out.println("I am from a thread!");
    }
	
    public static void main(String args[]) {
        (new Thread(new MyRunnable())).start();
    }
}

Subclassing Thread class: The Thread class itself implements Runnable, though its run method does nothing. Your class can subclass Thread class, providing its own implementation of run, as shown in the below example:

public class MyThread extends Thread {

    public void run() {
        System.out.println("I am from a thread!");
    }
	
    public static void main(String args[]) {
        (new MyThread()).start();
    }
}

What is the initial state of a thread when it is started ?

When the thread is created and started, initially it will be in the ready state. This state signifies that the thread is ready for execution. From here, it can be in the running state.

What is the difference between Thread and Process?

Both processes and threads are independent sequences of execution. The thread is a subset of Process, in other words, one process can contain multiple threads. Different processes run on separate memory spaces whereas all threads share the same memory space within a process. That’s why thread is light-weight and process is heavy-weight. Threads are an operating environment feature, rather than a CPU feature. A process has at least one thread of execution. Each process is started with a single thread, often called the primary thread, but can create additional threads from any of its threads. Each thread maintains exception handlers, a scheduling priority, thread local storage, a unique thread identifier, and a set of structures the system will use to save the thread context until it is scheduled.

Can a Java thread object invoke start method twice ?

No. We cannot invoke start() method twice. It will throw an exception – java.lang.IllegalThreadStateException

Consider below example

package com.roytuts.java.threads;

public class ThreadInvokeStartTwice extends Thread {

	@Override
	public void run() {
		System.out.println("Thread running : " + Thread.activeCount());
	}

	public static void main(String[] args) {
		ThreadInvokeStartTwice threadInvokeStartTwice = new ThreadInvokeStartTwice();
		threadInvokeStartTwice.start();
		threadInvokeStartTwice.start(); //Exception at this line
	}

}

Output

Exception in thread "main" Thread running : 2
java.lang.IllegalThreadStateException
	at java.lang.Thread.start(Thread.java:705)
	at com.roytuts.java.threads.ThreadInvokeStartTwice.main(ThreadInvokeStartTwice.java:13)

What does sleep() method do in thread?

Thread.sleep causes the current thread to suspend execution for a specified period. This is an efficient means of making processor time available to the other threads of an application or other applications that might be running on a computer system.

Two overloaded versions of sleep are provided: one that specifies the sleep time to the millisecond and one that specifies the sleep time to the nanosecond. However, these sleep times are not guaranteed to be precise, because they are limited by the facilities provided by the underlying OS. Also, the sleep period can be terminated by interrupts, so you cannot assume that invoking sleep will suspend the thread for precisely the time period specified.

What is the difference between wait and sleep methods in Java ?

sleep

It is a static method on Thread class. Thread.sleep causes the current thread to suspend execution for a specified period. During this time, the thread keeps the lock it has acquired. Two overloaded versions of sleep are provided: one that specifies the sleep time to the millisecond and one that specifies the sleep time to the nanosecond.

wait

It is a method on Object class. It causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object.
The current thread must own this object’s monitor. The thread releases ownership of this monitor and waits until another thread notifies threads waiting on this object’s monitor to wake up either through a call to the notify method or the notifyAll method. The thread then waits until it can re-obtain ownership of the monitor and resumes execution.

What is race condition ?

A race condition occurs when multiple threads access the shared data and they try to change the data at the same time. The thread scheduling algorithm can swap between threads at any time unexpectedly and you do not know the order when threads will attempt to access the shared data. Therefore, the result of the change in data is dependent on the thread scheduling algorithm, i.e. both threads are in a race to access/change the data.

Problems often occur when one thread access the data depending upon some condition and another modifying the data at the same time. For example, a thread checking some condition for doing an operation (if counter is 5 then make counter equals to 0) and at the same time another thread already was doing some changes to the counter variable then we may not get the expected output of the counter variable.

if ( counter == 5 ) { //thread one checking condition
    counter = 0;  //thread one will make counter's value 0. at the same time another thread was doing some changes to the counter's value
}

From the above code block we do not know what would be the value of counter variable and it could be anything. We have no idea of it.

In order to prevent such race condition, we will typically put a lock around the shared data to ensure only one thread can access the data at a time.

//acquire lock for counter
if ( counter == 5 ) { //thread one checking condition
    counter = 0;  //thread one will make counter's value 0
}
//release lock for counter

This way when a thread acquires a lock and doing some operation on the shared data then other thread will wait until the thread which acquired lock previously releases the lock and next waiting thread will acquire the lock and so on.

What is interrupt in thread?

An interrupt is an indication to a thread that it should stop what it is doing and do something else. It’s up to the programmer to decide exactly how a thread responds to an interrupt, but it is very common for the thread to terminate. A thread sends an interrupt by invoking interrupt on the Thread object for the thread to be interrupted. For the interrupt mechanism to work correctly, the interrupted thread must support its own interruption.

How does a thread support its own interruption?

This depends on what it’s currently doing. If the thread is frequently invoking methods that throw InterruptedException, it simply returns from the run method after it catches that exception.

What if a thread goes a long time without invoking a method that throws InterruptedException?

It must periodically invoke Thread.interrupted, which returns true if an interrupt has been received.

What does join() method do in thread?

The join method allows one thread to wait for the completion of another. If t is a Thread object whose thread is currently executing,

t.join();

causes the current thread to pause execution until t’s thread terminates.

Overloads of join allow the programmer to specify a waiting period. However, join is also dependent on the OS for timing, so you cannot not assume that join will wait exactly as long as you specify. Like sleep, join method responds to an interrupt by exiting with an InterruptedException.

What is daemon thread ?

Thread(s) with very low priority is/are running in background to perform different tasks like handling requests or various cron jobs that can exist in an application. A daemon thread is a thread, that does not prevent the JVM from exiting when the program finishes but the thread is still running. An example for a daemon thread is the garbage collection. You can use the setDaemon() method to change the Thread daemon properties.

Does each thread in Java use separate stack ?

Yes. It is a runtime stack but they share the same memory area.

What is synchronization?

Threads communicate primarily by sharing access to fields and the objects reference fields refer to. This form of communication is extremely efficient, but makes two kinds of errors possible: thread interference and memory consistency errors. This is where synchronization comes to rescue from such kind of errors.

However, synchronization can introduce thread contention, which occurs when two or more threads try to access the same resource simultaneously and cause the Java runtime to execute one or more threads more slowly, or even suspend their execution. Starvation and livelock are forms of thread contention.

What is interference in thread?

Interference happens when two operations, running in different threads, but acting on the same data randomly. Thus the two operations consist of multiple steps, and the sequences of steps overlap.

When does memory consistency error occur?

Memory consistency errors occur when different threads have inconsistent views of what should be the same data. A programmer may not need to know all these errors but he/she may need to know a strategy for avoiding such errors. The key to avoiding memory consistency errors is understanding the happens-before relationship. This relationship is simply a guarantee that memory writes by one specific statement are visible to another specific statement.

Synchronized methods enable a simple strategy for preventing thread interference and memory consistency errors: if an object is visible to more than one thread, all reads or writes to that object’s variables are done through synchronized methods.

What are synchronization idioms provided by Java language?

The Java programming language provides two basic synchronization idioms: synchronized methods and synchronized statements.

Explain synchronized methods

In order to make a method synchronized, simply you need to add the synchronized keyword to its declaration as shown below:

public class SynchronizedMethodExample {
    private int counter = 0;

    public synchronized void increment() {
        counter++;
    }

    public synchronized void decrement() {
        counter--;
    }

    public synchronized int value() {
        return counter;
    }
}

Let’s say we have created one instance from the above class SynchronizedMethodExample and when multiple threads are accessing the synchronized methods through this instance then synchronized methods have two effects:

  1. When one thread is executing a synchronized method for an object, all other threads that invoke synchronized methods for the same object block (suspend execution) until the first thread is done with the object.
  2. When a synchronized method exits, it automatically establishes a happens-before relationship with any subsequent invocation of a synchronized method for the same object. This guarantees that changes to the state of the object are visible to all threads.

Note: constructors cannot be synchronized — using the synchronized keyword with a constructor is a syntax error. Synchronizing constructors doesn’t make sense, because only the thread that creates an object should have access to it while it is being constructed.

What do you mean by Intrinsic Locks?

Synchronization is built around an internal entity known as the intrinsic lock or monitor lock. The API specification often refers to this entity simply as a “monitor”. Intrinsic locks play a role in both aspects of synchronization: enforcing exclusive access to an object’s state and establishing happens-before relationships that are essential to visibility.

Every object has an intrinsic lock associated with it. A thread that needs exclusive and consistent access to an object’s fields has to acquire the object’s intrinsic lock before accessing them, and then release the intrinsic lock when it’s done with them. A thread is said to own the intrinsic lock between the time it has acquired the lock and released the lock. As long as a thread owns an intrinsic lock, no other thread can acquire the same lock. The other thread will block when it attempts to acquire the lock.

When a thread releases an intrinsic lock, a happens-before relationship is established between that action and any subsequent acquisition of the same lock.

Does a lock acquired by thread get released when exception occurs in synchronized method?

Yes.

How does a thread acquires lock for static synchronized method?

Since a static method is associated with a class, not an object. In this case, the thread acquires the intrinsic lock for the Class object associated with the class. Thus access to class’s static fields is controlled by a lock that’s distinct from the lock for any instance of the class.

What is synchronized statement?

Unlike a synchronized method, a synchronized statement must specify the object that provides the intrinsic lock. A synchronized keyword is added around the critical section of code snippets. The below example shows how to apply a synchronized keyword around a block of statements:

public void addEmail(String email) {
    synchronized(this) {
        alternateEmail = email;
        emailCount++;
    }
    emailList.add(email);
}

What is reentrant synchronization?

Let’s consider, a thread cannot acquire a lock owned by another thread. But the thread can acquire a lock that it already owns. Allowing the thread to acquire the same lock more than once enables reentrant synchronization. This describes a situation where synchronized code, directly or indirectly, invokes a method that also contains synchronized code, and both sets of code use the same lock. Without reentrant synchronization, synchronized code would have to take many additional precautions to avoid having a thread cause itself to block.

What is atomicity?

An atomicity is an action where it either happens completely, or it doesn’t happen at all and no side effects of an atomic action are visible until the action is complete.

An increment expression, such as counter++, does not describe an atomic action. Even very simple expressions can define complex actions that can decompose into other actions. However, there are actions you can specify that are atomic:

  1. Reads and writes are atomic for reference variables and for most primitive variables (all types except long and double).
  2. Reads and writes are atomic for all variables declared volatile (including long and double variables).

Does atomicity remove need for synchronization?

No. Because memory consistency errors are still possible.

What is volatile?

Using volatile variables reduces the risk of memory consistency errors, because any write to a volatile variable establishes a happens-before relationship with subsequent reads of that same variable. This means that changes to a volatile variable are always visible to other threads. Any write to volatile variable goes to main memory. Any read to volatile variable happens from main memory. Value of the volatile variable never gets cached. Volatile is needed to develop embedded systems or device drivers where you need to write or read a memory-mapped device driver.

Example of volatile variable:

private volatile int counter = 0;

The Java VM and the CPU are allowed to reorder instructions in the program for performance reasons, as long as the semantic meaning of the instructions remain the same. However, instruction reordering does not happen with a volatile variable.

What is liveness?

A concurrent application’s ability to execute in a timely manner is known as its liveness.

When does deadlock occur?

Deadlock describes a situation where two or more threads are blocked forever, waiting for each other. Java deadlock situation arises with at least two threads and two or more resources.

What is starvation?

Starvation is much less problematic than deadlock, but is still problem that every designer of concurrent software is likely to encounter.

Starvation describes a situation where a thread is unable to gain regular access to shared resources and is unable to make progress. This happens when shared resources are made unavailable for long periods by “greedy” threads.

For example, suppose an object provides a synchronized method that often takes a long time to return. If one thread invokes this method frequently, other threads that also need frequent synchronized access to the same object will often be blocked.

What is livelock?

Like starvation this one is also much less problematic than deadlock, but is still problem that every designer of concurrent software is likely to encounter.

A thread often acts in response to the action of another thread. If the other thread’s action is also a response to the action of another thread, then livelock may result. As with deadlock, livelocked threads are unable to make further progress. However, the threads are not blocked — they are simply too busy responding to each other to resume work.

This is comparable to two people attempting to pass each other in a corridor: Liton moves to his left to let Arup pass, while Arup moves to his right to let Liton pass. Seeing that they are still blocking each other, Liton moves to his right, while Arup moves to his left.

What is Immutable Object?

An object is considered immutable if its state cannot be changed once it is constructed. Maximum reliability on immutable objects is widely accepted as a sound strategy for creating simple, reliable code.

Immutable objects are particularly useful in concurrent applications. Since their states cannot be changed, they cannot be corrupted by thread interference or observed in an inconsistent state.

How do you create immutable objects in Java?

The following rules define a simple strategy for creating immutable objects. Not necessarily all immutable classes follow these rules.

1. Declare the class as final. It doesn’t allow subclasses to override methods. A more sophisticated approach is to make the constructor private and construct instances in factory methods.

2. Make all fields final and private.

3. Don’t provide “setter” methods that modify fields or objects referred to by fields.

4. If the instance fields include references to mutable objects, don’t allow those objects to be changed:

Don’t provide methods that modify the mutable objects.

Don’t share references to the mutable objects. Never store references to external, mutable objects passed to the constructor; if necessary, create copies, and store references to the copies. Similarly, create copies of your internal mutable objects when necessary to avoid returning the originals in your methods.

What is Lock in Java?

Please refer tutorial https://www.jeejava.com/lock-in-java/

What are the differences between Lock and Synchronization?

Please refer tutorial https://www.jeejava.com/lock-in-java/

What are Executors?

A task is being done by a new thread, as defined by its Runnable object, or the thread itself, as defined by a Thread object. This works well for small applications, but in large-scale applications, it makes sense to separate thread management and creation from the rest of the application. Objects that encapsulate these functions are known as executors.

What are Executor Interfaces?

The java.util.concurrent package defines three executor interfaces:

  1. Executor, a simple interface that supports launching new tasks.
  2. ExecutorService, a subinterface of Executor, which adds features that help manage the lifecycle, both of the individual tasks and of the executor itself.
  3. ScheduledExecutorService, a subinterface of ExecutorService, supports future and/or periodic execution of tasks.

Typically, variables that refer to executor objects are declared as one of these three interface types.

What is Executor Interface?

The Executor interface provides a single method, execute, designed as a replacement for a common thread-creation idiom. If r is a Runnable object, and e is an Executor object you can replace

(new Thread(r)).start();

with

e.execute(r);

However, the low-level idiom creates a new thread and launches it immediately. Depending on the Executor implementation, execute may do the same thing, but is more likely to use an existing worker thread to run r, or to place r in a queue to wait for a worker thread to become available.

What is ExecutorService Interface?

The ExecutorService interface also executes with a similar, but with submit method. Like execute, submit accepts Runnable objects, but also accepts Callable objects, which allow the task to return a value. The submit method returns a Future object, which is used to retrieve the Callable return value and to manage the status of both Callable and Runnable tasks.

ExecutorService also provides methods for submitting large collections of Callable objects. Finally, ExecutorService provides a number of methods for managing the shutdown of the executor. To support immediate shutdown, tasks should handle interrupts correctly.

What is ScheduledExecutorService Interface?

The ScheduledExecutorService interface provides the methods of its parent ExecutorService with schedule, which executes a Runnable or Callable task after a specified delay. In addition, the interface defines scheduleAtFixedRate and scheduleWithFixedDelay, which executes specified tasks repeatedly, at defined intervals.

What is Thread Pool and why it is used?

A thread pool is a pool of worker threads and used by most of the executor implementations in java.util.concurrent. This kind of thread exists separately from the Runnable and Callable tasks it executes and is often used to execute multiple tasks.

Using worker threads in thread pool minimizes the overhead of thread creation. Thread objects use a significant amount of memory, and in a large-scale application, allocating and deallocating many thread objects creates a significant memory management overhead.

One mostly used common type of thread pool is the fixed thread pool. This type of pool always has a specified number of threads running; if a thread is somehow terminated while it is still in use, it is automatically replaced with a new thread. Tasks are submitted to the pool via an internal queue, which holds extra tasks whenever there are more active tasks than threads.

Please refer to the tutorial Creating Custom Thread Pool in Java

What is Fork/Join?

This is a new feature added to Java API version 1.7. The fork/join framework is an implementation of the ExecutorService interface that helps you take advantage of multiple processors. It is designed for work that can be broken into smaller pieces recursively. The goal is to use all the available processing power to enhance the performance of your application.

As with any ExecutorService implementation, the fork/join framework distributes tasks to worker threads in a thread pool. The fork/join framework is distinct because it uses a work-stealing algorithm. Worker threads that run out of things to do can steal tasks from other threads that are still busy.

The center of the fork/join framework is the ForkJoinPool class, an extension of the AbstractExecutorService class. ForkJoinPool implements the core work-stealing algorithm and can execute ForkJoinTask processes.

What are Concurrent Collections in Java?

The java.util.concurrent package includes a number of additions to the Java Collections Framework. These are most easily categorized by the collection interfaces provided:

  1. BlockingQueue defines a first-in-first-out data structure that blocks or times out when you attempt to add to a full queue, or retrieve from an empty queue. Please refer to BlockingQueue
  2. ConcurrentMap is a subinterface of java.util.Map that defines useful atomic operations. These operations remove or replace a key-value pair only if the key is present, or add a key-value pair only if the key is absent. Making these operations atomic helps avoid synchronization. The standard general-purpose implementation of ConcurrentMap is ConcurrentHashMap, which is a concurrent analog of HashMap. Please refer to ConcurrentHashMap
  3. ConcurrentNavigableMap is a subinterface of ConcurrentMap that supports approximate matches. The standard general-purpose implementation of ConcurrentNavigableMap is ConcurrentSkipListMap, which is a concurrent analog of TreeMap.

All of these collections help avoid Memory Consistency Errors by defining a happens-before relationship between an operation that adds an object to the collection with subsequent operations that access or remove that object.

What are Atomic Variables?

The java.util.concurrent.atomic package defines classes that support atomic operations on single variables. We know that one way to gaurd the critical section’s code is to use the synchronization. But for a more complicated class, we might want to avoid the liveness impact of unnecessary synchronization.

For example, consider following example:

class NormalCounter {
    private int counter = 0;

    public void increment() {
        counter++;
    }

    public void decrement() {
        counter--;
    }

    public int value() {
        return counter;
    }
}

Make counter variable safe from thread interference using synchronization:

class SynchronizedCounter {
    private int counter = 0;

    public synchronized void increment() {
        counter++;
    }

    public synchronized void decrement() {
        counter--;
    }

    public synchronized int value() {
        return counter;
    }
}

Replacing the int field with an AtomicInteger allows us to prevent thread interference without resorting to synchronization as shown below:

import java.util.concurrent.atomic.AtomicInteger;

class AtomicCounter {
    private AtomicInteger counter = new AtomicInteger(0);

    public void increment() {
        counter.incrementAndGet();
    }

    public void decrement() {
        counter.decrementAndGet();
    }

    public int value() {
        return counter.get();
    }
}

What is ThreadLocalRandom?

java.util.concurrent package includes a convenience class ThreadLocalRandom in Java API version 1.7 for applications that expect to use random numbers from multiple threads or ForkJoinTasks.

For concurrent access, using ThreadLocalRandom instead of Math.random() results in less contention and better performance.

All you need to do is call ThreadLocalRandom.current(), then call one of its methods to retrieve a random number. For example:

int r = ThreadLocalRandom.current() .nextInt(4, 77);

When does race condition occur in HashMap?

Write operation on HashMap can trigger resizing of HashMap and if re-size is done by multiple thread at same time that could lead to race condition in HashMap.

How Collections.SynchronizedMap and ConcurrentHashMap lock the Map?

Collections.SynchronizedMap lock the entire Map while Concurrent Map only locks portion of Map.

ConcurrentHashMap vs HashTable or Collections.SynchronizedHashMap?

ConcurrentHashMap is more scalable than hashtable or SynchronizedMap. Also ConcurrentHashMap only locks portion of Map while SynchronizedMap and hashtable locks whole map which makes ConcurrentHashMap faster than hashtable or SynchronizedMap.
Iterator of ConcurrentHashMap‘s keySet area also fail-safe and doesn’t throw ConcurrentModificationExceptoin.

How can I ensure thread safety using array list?

By using CopyOnWriteArrayList. Collections.synchronizedList will be better, but CopyOnWriteArrayList will be your best bet.
CopyOnWriteArrayList implementations provide much higher concurrency while preserving thread safety, with some minor compromises in its promises to callers. Many concurrent applications will benefit from its use.

What is ‘single collection wide lock’?

‘single collection wide lock’ means whole collection will be locked when one thread does read, write or update. This means even if two threads are reading values, they will feel contention. This is optimized in ConcurrentHashMap and CopyOnWriteArrayList by using sophisticated technique of portion wide lock and copying the collection when change to give better performance and scalability.

How to make a class thread safe without synchronization?

No need for synchronization at all if you don’t have mutable state.
No need for synchronization if the mutable state is confined to a single thread. This can be done by using local variables or java.lang.ThreadLocal
You can also use built-in synchronizers. java.util.concurrent.locks.ReentrantLock has the same functionality as the lock you access when using synchronized blocks and methods, and it is even more powerful.

What are the run and start methods of Thread class?

start method is used to start a new thread whereas a run method is used to run the already started thread.

What is the difference between Runnable and Callable?

Both Runnable and Callable are used to execute tasks on threads. The Runnable was added to JDK 1.2 whereas Callable was added to JDK 1.5. The important difference is Callable returns value (the Future object that holds the result of computed value) and throws Exception whereas Runnable neither returns any value nor throws any Exception.

What is difference between CountDownLatch and CyclicBarrier?

Both CountDownLatch and CyclicBarrier wait on one or more threads to finish their tasks before a main thread gets chance to start work. The main difference is you cannot reuse the CountDownLatch once count reaches to zero whereas CyclicBarrier can be reused even after barrier is broken.

You can find more information on CountDownLatch and CyclicBarrier.

What is thread safety?

Thread safety, is a property of an object or Java code, that guarantees the expected behaviour when executed or used by multiple threads in any manner. For example, a thread-safe counter object will not miss any count if same instance of that counter is shared among multiple threads.

Can you stop a thread?

Currently there is no way to stop a thread once it starts. It stops automatically when a thread finishes its execution when it finishes execution of run or call method. There were some methods, such as stop, resume, suspend, available in JDK 1.0 but due to deadlock threats these were removed from later version.

Can you share data between two threads? If yes, how?

Yes. You can use BlockingQueue to share data between two threads. You can find more details on BlockingQueue.

What is ThreadLocal and why is it used?

You can find details at How ThreadLocal works in Java

What is Future?

A Future represents the pending result of an asynchronous computation. It offers a method — get — that returns the result of the computation when it’s done.

You will find more details at Callable and Future

What is CompletableFuture?

A Future represents the pending result of an asynchronous computation. It offers a method — get — that returns the result of the computation when it’s done.

The problem is that a call to get is blocking until the computation is done. This is quite restrictive and can quickly make the asynchronous computation pointless.

CompletableFuture<T> extends Future<T> and makes it completable in an ad hoc manner. This is a big deal, considering that Future objects were limited before Java 8.

This new and improved CompletableFuture has mainly two benefits:

It can be explicitly completed by calling the complete() method without any synchronous wait. It allows values of any type to be available in the future with default return values, even if the computation did not complete, using default/intermediate results.

It also allows you to build a pipeline data process in a series of actions. You can find a number of patterns for CompletableFutures such as creating a CompletableFuture from a task, or building a CompletableFuture chain.

You can find more details here at CompletableFuture in Java

For what reason you should check wait condition in a loop?

It is possible for a waiting thread to receive false alerts that’s apparently but actually not valid. If you do not check the waiting condition in loop, the thread will simply exit even if condition is not met because, when a waiting thread wakes up, it cannot assume the state it was waiting for is still valid. The state might have been valid in the past, and it may have been changed after the notify() method was called but before the waiting thread woke up. That’s why it is always better to call wait() method in loop.

What is the difference between notify() and notifyAll()?

The methods are used for notifying waiting threads to perform task. Java does not provide any mechanism to choose which particular thread will be notified further to finish a task. That’s why when you know there is only one thread waiting then you can use notify() method to wake up for finishing the pending task. On the other hand you can use notifyAll() method to all waiting threads to wake up but at a time only one thread will proceed further to finish a task.

Why wait(), notify() and notifyAll() are in Object class?

In the Java language, a thread wait()s on a particular instance of Object – a monitor assigned to that object to be precise. If you want to send a signal to one thread that is waiting on that specific object instance then you call notify() on that object. If you want to send a signal to all threads that are waiting on that object instance, you use notifyAll() on that object.

If wait(), notify() and notifyAll() were in the Thread class instead, then each thread would have to know the status of every other thread.

How would thread t1 know that thread t2 was waiting for access to a particular resource? If thread t1 needed to call thread t2.notify() it would have to somehow find out that thread t2 was waiting for access to a resource. There would need to be some mechanism for threads to register the resources or actions that they need so others could send signal to them when resource was ready or available.

In Java, the object, itself is the entity that is shared between threads, which allows them to communicate with each other. The threads have no specific knowledge of each other and they can run asynchronously or concurrently. Further They don’t need to know each other status. They run and they lock, wait, and notify on the object that they want to get access to. They don’t need to know that it is thread t1 or thread t2 which is waiting for the resource. They just notify on the resource and for whomever it is waiting will be notified.

That’s why Java provides lock at object level not at thread level. Every object has lock, which is acquired by thread. If thread needs to wait for certain lock then it make sense to call wait() on that object rather than on that thread.

Why wait(), notify() and notifyAll() methods are kept inside synchronized block?

We all know that wait(), notify() and notifyAll() methods are used for inter-threaded communications. To get rid of missed signal and spurious wake up problems, waiting thread always waits on some conditions. Check my above question – For what reason you should check wait condition in a loop?

boolean notified = false;

while(!notified) {
	wait();
}

Every thread has its local cache, where all the changes first get written there and then reflected to the main memory gradually.

Had these methods not invoked within synchronized block, the notified variable would not have been flushed into the main memory and would have remained there in thread’s local cache memory. So the waiting thread will keep waiting for the signal forever though it was reset by notifying thread.

To fix these types of problems, these methods are always invoked inside synchronized block. As soon as synchronized block starts, it makes sure that everything will be read from main memory and will be flushed into main memory before (happens-before mechanism) exiting the synchronized block.

// locks should be final objects so the object instance we are synchronizing on, never changes
private final Object lock = new Object();
...
// ensure that the thread has a mutex lock on some key code
synchronized (lock) {

    boolean notified = false;
    
	// need to wait for other threads to finish with some resource
	//this releases the lock and waits on the associated monitor
	while(!notified) {
        wait();
    }
	
	// need to signal another thread that some state has changed and they can awake and continue to run
	lock.notify();
}

Synchronized vs Concurrent collections in Java?

Both synchronized and concurrent collections provide thread-safety for multi-threaded environment for concurrent access but later provides scalability than former. Prior to Java 1.5, Java programmers had only synchronized collection for multi-threaded environment, which hampered scalability of the system. Java 1.5 introduced concurrent collections like ConcurrentHashMap, ConcurrentNavigableMap, BlockingQueue, which not only provide thread-safety but also improve scalability by using block level locking by partitioning the internal table.

How to avoid dead lock in Java?

Don’t use multiple threads – unless required, do not share data over multiple threads. If data can’t be changed after creation/initialization, stick to final variables. If you can’t avoid shared data among multiple threads, use granular synchronized blocks or Locks.

Don’t hold several locks at once. If you need to do, always acquire the locks in the same order. If you are using only synchronized code blocks, make sure that locks are acquired/released in a certain order.

Avoid nested locks. This is almost similar to the above point. This is the most common reason for deadlock. Avoid locking another resource if you already hold one.It is almost impossible to get deadlock if you are working with only one object lock.

Do not wait indefinitely.

Probably you should not make your lock public. There is no telling what can happen if you do this as anyone would be able to acquire the lock without the object knowing (this is also why you shouldn’t lock this). If you keep your lock private then you have complete control and this makes it more manageable.

How do you check whether a thread holds lock or not?

There is a method called holdsLock() on java.lang.Thread. Thread.holdsLock(lock) method returns true if and only if the current thread holds the monitor lock on the specified object.

What is the difference between synchronized and ReentrantLock?

ReentrantLock is mutual exclusive lock, similar to implicit locking provided by synchronized keyword in Java, with extended feature like fairness, which can be used to provide lock to longest waiting thread. Lock is acquired by lock() method and held by Thread until a call to unlock() method. Fairness parameter is provided while creating instance of ReentrantLock in constructor. ReentrantLock provides same visibility and ordering guarantee, provided by implicitly locking, which means, unlock() happens before another thread get lock().

Main difference between synchronized and ReentrantLock is ability to trying for lock interruptibly, and with timeout. Thread doesn’t need to block infinitely, which was the case with synchronized.

ReentrantLock provides convenient tryLock() method, which acquires lock only if its available or not held by any other thread.

ReentrantLock also provides convenient method to get List of all threads waiting for lock.

You will also find difference between synchronized and Lock.

What could be the cause of RejectedExecutionException?

From ThreadPool JavaDoc, new tasks submitted in method execute(java.lang.Runnable) will be rejected when the Executor has been shut down, and also when the Executor uses finite bounds for both maximum threads and work queue capacity, and is saturated. In either case, the execute method invokes the RejectedExecutionHandler.rejectedExecution(java.lang.Runnable, java.util.concurrent.ThreadPoolExecutor) method of its RejectedExecutionHandler.

In other words, ThreadPoolExecutor’s submit() method throws RejectedExecutionException if the task cannot be scheduled for execution.

What is the difference between submit() and execute() method in thread pool?

The purpose of both methods are to submit tasks into thread pools. The void execute() method is in Executor interface and executes given task in future. On other hand, submit() declared in ExecutorService interface, which extends Executor interface is an overloaded method. It takes either Runnable or Callable task and can return Future object which can hold the pending result of computation.

What is ReadWriteLock and how do you write it in Java?

Please refer to the example ReadWriteLock in Java

How to create thread-safe Singleton class in Java?

Please refer to the example How Singleton pattern works in Java

What is Fork and Join in Java?

The ForkJoinPool was added to Java 1.7 and available under the java.util.concurrent package. It is similar to the Java ExecutorService with one difference. The ForkJoinPool makes it easy for tasks to split their work up into smaller tasks which are then submitted to the ForkJoinPool too. Tasks can keep splitting their work into smaller sub-tasks for as long as it makes to split up the task. It is designed for tasks that can be broken into smaller pieces recursively. The goal is to use all the available processing power to enhance the performance of your application.

Fork

A task that uses the fork and join principle can fork (split) itself into smaller sub-tasks which can be executed concurrently.

Join

When a task has split itself up into sub-tasks, the task waits until the sub-tasks have finished executing. Once the sub-tasks have finished executing, the task may join (merge) all the results into one result.