-
Can the keys in Hashing data structure be made Mutable?
-
The answer is NO. If we make the keys mutable then the hashcode() of the key will no more be consistent over time which will cause lookup failure for that object from the data structure.
-
As bucket location is calculated based on key’s hashcode(), any change in key value will the change the hashCode calculation so is the problem in finding the stored object.
-
Never make changes to the hashmap’s key, otherwise the associated object can not be fetched using get() method.
-
Though it will be accessible using other methods which iterate over the entire collection.
-
-
Here are 1 million trades, you need to check if a given trade exists in those trades or not. Which Collection would you chose to store those 1 million trades and why?
-
We can use HashSet. HashSet is based on hashTable(Map) data structure. The time complexity of fetching an element is O(1) using get(). So it’s the most efficient way.
-
-
In a program, multiple threads are creating thousands of large temporary StringBuilder objects. Life span of all those objects is 1 GC cycle. But somehow we are getting JVM pauses in our application. How would you troubleshoot the problem ?
-
Java uses 4 types of garbage collector : Serial, parallel, CMS , G1. Each collector uses different assumption and algorithms to clear the garbage.
-
Out of above four, G1 is the most promising. The G1 collector utilizes multiple background threads to scan through the heap that it divides into regions, spanning from 1MB to 32MB. G1 collector is geared towards scanning those regions that contain the most garbage objects first
-
This collector is turned on using the –XX:+UseG1GC flag.
-
This strategy reduced the chance of the heap being depleted before background threads have finished scanning for unused objects.
-
It compacts the heap on-the-go, something the CMS collector only does during full STW collections.
-
Another feature of G1 collectore is String deduplication: a new optimization has been made that enables the G1 collector to identify strings which are duplicated more than once across your heap and correct them to point into the same internal char[] array,
-
To avoid multiple copies of the same string from residing inefficiently within the heap.
-
We can use -XX:+UseStringDeduplicationJVM argument to try this out.
-
What is difference between Callable and Runnable Interface?
-
Callable interface is similar to Runnable, in that both are designed for classes whose instances are potentially executed by another thread.
-
A Runnable, however, does not return a result and cannot throw a checked exception.
-
In order to convert Runnable to Callable use the following utility method provided by Executors class
-
Callable callable = Executors.callable(Runnable task);
-
Callable, however must be executed using a ExecutorService instead of Thread as shown below.
result = exec.submit(aCallable).get();
-
Submitting a callable to ExecutorService returns Future Object which represents the lifecycle of a task and provides methods to check if the task has been completed or cancelled, retrieve the results and cancel the task.
- Best way for storing currency values in Java ?
-
We could either use int/long or BigDecimal. int and long can be used if decimal precision is not required. We can’t use float or double due to inconsistency in storing them in the memory.
-
BigDecimal represents a signed decimal number of arbitrary precision with an associated scale.
-
BigDecimal provides full control over the precision and rounding of the number value.
-
We should really avoid using BigDecimal(double value) constructor instead prefer BigDecimal(String).
-
Use BigDecimal ("0.1") which is equal to exactly 0.1 instead of using BigDecimal (0.1) which may result in 0.100000…5..3.
- What is AtomicInteger and how it is useful in concurrent environment?
-
AtomicInteger uses combination of ''volatile ''& ''CAS ''(compare and swap) to achieve thread-safety for Integer Counter.
-
AtomicInteger class stores its value field in a volatile variable, thus it is a decorator over the traditional volatile variable,but it provides unique non-blocking mechanism for updating the value after requiring the hardware level support for CAS.
-
Under low to moderate thread contention, atomic updates provides higher throughput compared to synchronized blocking increment operation.
-
Thread contention:
-
Essentially thread contention is a condition where one thread is waiting for a lock/object that is currently being held by another thread.
-
Waiting thread cannot use that object until the other thread has unlocked that particular object.
- Iterator Interface provides a remove() method but no add() method, Why?
-
-
The sole purpose of an Iterator is to enumerate through a collection. Iterator is top level interface,having a add() would be only sensible if underlying data structure is ordered(i.e. list)
-
Adding element to Set using Iterator add(), the trouble is that no predicting "where" the insert is going to happenwrt the current "position" of the iterator in the set.
-
Similarly TreeSet maintains the order of elements in by implementation Red-Black Tree data structure. Therefore if we tried to add an element to TreeSet using the iterator at given index or position of iterator. it might corrupt the state of the underlying data structure.
-
Iterator Vs listIterator(in terms of presence of add () method)
-
ListIterator provide the add() methods because it know the location where it needs to add the newly created element as List preserves the order of its element in order of their insertion.
- How to create and avaoid deadlock?
-
https://javarevisited.blogspot.com/2018/08/how-to-avoid-deadlock-in-java-threads.html
- Producer-consumer using blockingqueue example ?
-
https://docs.oracle.com/javase/8/docs/api/?java/util/concurrent/BlockingQueue.html
class Producer implements Runnable {
private final BlockingQueue queue;
Producer(BlockingQueue q) { queue = q; }
public void run() {
try {
while (true) { queue.put(produce()); }
} catch (InterruptedException ex) { ... handle ...}
}
Object produce() { ... }
}
class Consumer implements Runnable {
private final BlockingQueue queue;
Consumer(BlockingQueue q) { queue = q; }
public void run() {
try {
while (true) { consume(queue.take()); }
} catch (InterruptedException ex) { ... handle ...}
}
void consume(Object x) { ... }
}
class Setup {
void main() {
BlockingQueue q = new SomeQueueImplementation();
Producer p = new Producer(q);
Consumer c1 = new Consumer(q);
Consumer c2 = new Consumer(q);
new Thread(p).start();
new Thread(c1).start();
new Thread(c2).start();
}
}
- What is Lock interface, various implementation and benefits ?
-
-
https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/locks/ReentrantLock.html
-
https://winterbe.com/posts/2015/04/30/java8-concurrency-tutorial-synchronized-locks-examples/
-
A reentrant mutual exclusion Lock with the same basic behavior and semantics as the implicit monitor lock accessed using synchronized methods and statements, but with extended capabilities.
-
A ReentrantLock is owned by the thread last successfully locking, but not yet unlocking it.
-
A thread invoking lock will return, successfully acquiring the lock, when the lock is not owned by another thread.
-
The constructor for this class accepts an optional fairness parameter. When set true, under contention, locks favor granting access to the longest-waiting thread.
-
class X {
private final ReentrantLock lock = new ReentrantLock();
// ...
public void m() {
lock.tryLock(); // this is a non-blocking version of lock() method; it attempts to acquire the lock immediately, return true if locking succeeds.
lock/.tryLock(long timeout, TimeUnit.SECONDS); // Acquires the lock if it is not held by another thread within the given waiting time and the current thread has not been interrupted.
try {
// ... method body
} finally {
lock.unlock()
}
}
}
-
We need to make sure that we are wrapping the lock() and the unlock() calls in the try-finally block to avoid the deadlock situations.
- What is ExecutorService ?
-
https://winterbe.com/posts/2015/04/07/java8-concurrency-tutorial-thread-executor-examples/
-
https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html
-
ExecutorService is a higher level replacement for working with threads directly.
-
Executors are capable of running asynchronous tasks and typically manage a pool of threads, so we don’t have to create new threads manually.
-
The easiest way to create ExecutorService is to use one of the factory methods of the Executors class.
ExecutorService executor = Executors.newSingleThreadExecutor(); // executor with a thread pool of size one.
or
ExecutorService executor = Executors.newFixedThreadPool(10); // create a thread-pool with 10 threads:
executor.submit(() -> {
String threadName = Thread.currentThread().getName();
System.out.println("Hello " + threadName);
});
-
ExecutorService will not be automatically destroyed when there is not task to process. It will stay alive and wait for new work to do.
-
To properly shut down an ExecutorService, we have the shutdown() and shutdownNow() APIs.
-
One of the preferred way to shutdown executor:
try {
System.out.println("attempt to shutdown executor");
executor.shutdown();
executor.awaitTermination(5, TimeUnit.SECONDS);
}
catch (InterruptedException e) {
System.err.println("tasks interrupted");
}
finally {
if (!executor.isTerminated()) {
System.err.println("cancel non-finished tasks");
}
executor.shutdownNow();
System.out.println("shutdown finished");
}
-
Future interface : The submit() and invokeAll() methods return an object or a collection of objects of type Future, which allows us to get the result of a task’s execution or to check the task’s status (is it running or executed).
Future<String> future = executorService.submit(callableTask);
String result = null;
try {
result = future.get(200, TimeUnit.MILLISECONDS); // a special blocking method get() which returns an actual result of the Callable task’s execution or null in the case of Runnable task
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
boolean isDone = future.isDone(); // To check whether assigned task has been completed or not
boolean canceled = future.cancel(true); // to cancel the execution of task
boolean isCancelled = future.isCancelled(); // to check the cancellation status
- What is deadlock, how to find and fix deadlock ?
-
-
https://javarevisited.blogspot.com/2018/08/how-to-avoid-deadlock-in-java-threads.html
-
how to find : If nested synchronized block or calling one synchronized method from other, or trying to get a lock on a different object or looking into java thread dump
-
public class DeadLockDemo {
/*
* This method request two locks, first String and then Integer
*/
public void method1() {
synchronized (String.class) {
System.out.println("Aquired lock on String.class object");
synchronized (Integer.class) {
System.out.println("Aquired lock on Integer.class object");
}
}
}
public void method2() {
synchronized (Integer.class) {
System.out.println("Aquired lock on Integer.class object");
synchronized (String.class) {
System.out.println("Aquired lock on String.class object");
}
}
}
}
-
Both threads will waiting for each other to release String and Integer lock to proceed further which will never happen.
-
how to avoid:
**Avoid nested locks
**Lock only what is required
**Avoid waiting indefinitely