What is the difference between synchronous and asynchronous programming?
In Synchronous programming, code will execute in a predictable sequential manner, starting one line or block of code after the previous one finished. In asynchronous programming, blocks of code are marked to execute in parallel and thus once an asynchronous block of code is invoked, it starts to execute simultaneously(in parallel) with the block of code that invoked it.
What is the difference between a thread and a process?
A process is a program in execution, while a thread represents a portion of code in execution. A process can make use of one thread or it can make use of multiple threads to run portions of code in parallel with another thread’s code. Creating and terminating a process has more overheads than a thread. A process has its own address space, while a thread uses the address space of the process that created it.
What is an immutable object?
An immutable object is one that is unable to change, once the object is constructed. To represent a different value, a new instance has to be created with the new value.
Why use multithreading in your applications?
Multithreading is commonly used to build a responsive user interface, for example, an installer which shows a progress bar to a user may be copying files using separate threads, and monitoring the progress through the main thread.
Multithreading is also commonly used to speed up processes which can be decomposed into sub-tasks that can be run simultaneously and to handle network communications effectively (example: listening to incoming connections on one thread while serving each request on a separate thread).
When to use Runnable and when to use Thread in Java?
Java allows single inheritance, however, a class can implement multiple interfaces. Since Runnable is an interface and Thread is a class if you need to have the implementing class inherit some behaviour from another class, the Runnable interface must be used.
How do you create a thread in Java?
The below code shows how to create a thread when inheriting the Thread Class
class AsyncCode extends Thread {
public void run() {
System.out.println("Do Something!");
}
}
AsyncCode asyncCode = new AsyncCode();
asyncCode.start();
//The below code shows how to create a thread when implementing the Runnable //Interface
class CodeBlock implements Runnable {
public void run() {
System.out.println("MultiThreading!");
}
}
During a thread’s lifetime, what states can it have?
State | Explanation | Code causing state |
New | The state of a newly created thread | Thread myCode = new Thread(); |
Runnable | The state of a started thread which is currently executing | myCode.start(); |
Blocked | Execution of a synchronized block which is already being executed by another thread | |
Waiting | A thread is currently waiting for another thread to invoke notify | Thread.wait() |
Timed Waiting | A thread is currently waiting for another thread to invoke notify with a set timeout in which case it will continue execution after either another thread invokes notify or the timeout has expired depending on which happens first | Thread.wait(100L) |
Terminated | When a thread execution has completed |
What is context switching?
A context switch occurs when execution switches from one thread to another. Context switching is the process where the program execution halts executing a thread and starts preparing to execute another thread by loading the code pointer and memory contents for the thread that will be executing.
Can you explain what is a thread scheduler
A thread scheduler switches between threads to allow multiple threads to execute on a single core. The java thread scheduler uses a numeric priority to schedule which threads execute first. When two threads with the same priority are waiting to execute, the thread to be executed will be selected arbitrarily.
What is ThreadLocal?
A ThreadLocal variable manages a different value of the specified variable for each created thread and allows the developer to have the value of the variable available even after execution has completed.
What is a Thread Pool? How can we create a Thread Pool in Java?
A thread pool is a collection of threads which are used whenever a thread is required and kept available for future use. This helps avoid the overhead of creating a thread and provides control over the number of threads that a process creates.
Using the java.util.concurrent.Executors, we create a Thread Pool using one of the various factory methods that provide the various thread pool implementations available. Some of the most commonly used implementations are:
Fixed Thread Pool → Which will apply a threshold for the maximum number of threads in the thread pool.
Cached Thread Pool → The cached thread pool will keep the threads available in the pool even after the threads are free for a threshold time to attempt to minimize the overhead of thread creation.
Single Thread Pool → The thread pool uses a single thread for execution.
Work Stealing Thread Pool → This thread pool tries to maximize the parallelization of the submitted tasks.
What is the Executors Framework?
The Executors framework is composed of three interfaces, namely:
- The Executor interface has a single method which provides the ability to submit a task for execution.
- The ExecutorService interface is a subinterface of the Executor and provides features to manage the lifecycle of tasks and the executor.
- The ScheduledExecutorService interface is a subinterface of the ExecutorService and provides additional features to submit tasks to execute in the future or periodically.
What is a race condition?
A race condition occurs when multiple threads operate on a shared resource at the same time and depend on the shared resource having a predefined state during the execution.
Since the thread scheduler can switch between threads at any time the execution order is unknown and thus the threads will end up ‘racing’ between them to find the resource in the required state to execute successfully.
The below code example demos how a race condition may occur
class Participant extends Thread {
static int x;
public void run() {
x = 5;
System.out.println("Race Started!");
x = x * 2;
if(x != 10) {
System.out.println("Lost!");
} else {
System.out.println("Won!");
}
}
}
If the code in the run method was executed synchronously it would print ‘Race Started!’ followed by ‘Won!’ every time, however since we do not know when the thread scheduler will switch between threads each thread may end up printing either ‘Won!’ or ‘Lost!’. In this example, the threads end up racing to initialize x and perform a computation.
What is synchronization?
Synchronization is a mechanism for demarking a block of code to be executed by a single thread at any point in time. If multiple threads require the execution of a synchronized block, the first to request execution will be allowed, while the remaining requests will be queued until execution of the block is completed by the first requesting thread. At this stage, the next thread waiting will be allowed to execute this block, until no more threads are waiting to execute the code block.
What is thread starvation?
Thread starvation is caused when multiple threads block to use a shared resource and one of the threads uses the resource frequently and keeps the resource blocked for a long time. This will cause other threads to have limited access and possibly be completely unable to execute due to the resource being unavailable.
To reduce the possibility of thread starvation, a developer can use the ReentrantLock class which provides an overloaded constructor that accepts a fairness boolean parameter. This class provides a mutual exclusion lock that when provided with a value of true in the constructor will favour the longest waiting thread when granting the lock.
What is a deadlock situation?
A deadlock occurs when two threads are waiting on each other to free a different resource before being able to proceed. For example thread(t1) has blocked a resource (r1) and is waiting for a resource(r2) to become available to proceed with the rest of the execution. At the same time thread(t2) has blocked the resource(r2) and is waiting for the resource (r1) to become available to also proceed with the rest of its execution. The two threads reach a state where they are both waiting on each other to complete and thus blocking without the possibility of continuing execution.
What is the use of the synchronized keyword? What is the difference between synchronized and volatile keywords?
The volatile keyword marks a variable such that any read and write operations are immediately reflected in main memory.
The synchronized keyword blocks execution for a demarcated code block to a single thread and synchronizes the thread memory with main memory when acquiring the lock and when releasing the lock
Explain the difference between sleep() and wait() methods.
With sleep, execution will stop for a predetermined time, unless the user invokes the interrupt method on the thread. When using sleep the thread will keep hold of any locks it owns.
When using wait the thread will stop executing until another thread invokes notify or notifyAll method. When invoking the wait method, the thread will release any locks it may hold. The wait method is overloaded and provides a method call which accepts a timeout parameter, in which case the thread will block until it is either notified or the timeout occurs, depending on which happens first.
What is Callable and Future?
A callable is an interface representing an asynchronous code which returns a result. A Future interface is a handle to the future result providing API calls for checking on the status of the callable execution and retrieving the result when the execution is complete.
class StringUUIDGenerator implements Callable<String> {
public String call() throws Exception {
System.out.println("Generating...");
return UUID.randomUUID().toString();
}
}
ExecutorService es = Executors.newSingleThreadExecutor();
Future<String> result = es.submit(new StringUUIDGenerator());
System.out.println(result.get());
What is the FutureTask class?
A FutureTask allows for the execution of an asynchronous code block. The class is an implementation of the Future interface and thus provides the ability to start execution, check the status of the thread, cancel execution and retrieve the result.
What is BlockingQueue? How can we implement the Producer-Consumer problem using Blocking Queue?
A BlockingQueue is an optionally size bound queue with additional operations which support waiting until either space within the queue becomes available for queueing operations or the queue stops being empty for de-queueing operations. A BlockingQueue implementation should not accept a null value since null values are used to indicate a failed operation.
A BlockingQueue supports 4 types of operations,
- Regular collection operations which throw an exception when the queue is empty or full.
- Operations which use boolean or null values to indicate success or failure of the operation
- Operations which accept an optional timeout and will return a boolean or null value to indicate success or failure, in which case the operation will wait for the specified timeout before returning a failure result. In the case that the operation can complete successfully within the timeout period, it will return immediately on success.
- Operations which block until either the queue can add an item or the queue is not empty and can remove an item from the queue.
The producer class implemented as a thread accepts a reference to a queue in the parameter and invokes the put method since this call will block if the queue becomes full. In this example, we are generating randomized longs.
class Producer extends Thread{
private final BlockingQueue<Long> queue;
public Producer(final BlockingQueue<Long> queue) {
this.queue = queue;
}
public void run() {
try {
while(true) {
Long value = new Random().nextLong();
queue.put(value);
System.out.println(String.format("Generated %d", value));
}
} catch (InterruptedException e) {
System.out.println("production interrupted!");
}
}
}
The consumer class is also implemented as a thread and accepts a reference to a queue in the parameter. In the implementation below the consumer will just show the generated number and invokes the take method. If the queue is empty the take method call will block until an element becomes available.
class Consumer extends Thread{
private final BlockingQueue<Long> queue;
public Consumer(final BlockingQueue<Long> queue) {
this.queue = queue;
}
public void run() {
try {
while(true) {
//Operation is blocking in case of empty queue
Long value = queue.take();
System.out.println(String.format("Consumed %d", value));
}
} catch (InterruptedException e) {
System.out.println("consumption interrupted!");
}
}
}
In this example, I create the blocking queue using the ArrayBlockingQueue, but any implementation will do. The maximum queue size is limited to 6 items. An arbitrary number of producers and consumers can be created as required.
BlockingQueue<Long> queue = new ArrayBlockingQueue<Long>(6);
Producer producer1 = new Producer(queue);
Producer producer2 = new Producer(queue);
Consumer consumer1 = new Consumer(queue);
producer1.start();
producer2.start();
consumer1.start();