Java Concurrency: synchronized Methods, Transient/Volatile Variables, and Varargs
This Java tutorial explores key concurrency concepts: the impact of `synchronized` methods on thread access, the differences between `transient` and `volatile` variables, and the use of varargs for flexible method arguments. Enhance your understanding of multithreaded programming in Java. Suitable for intermediate Java developers.
Advanced Java Interview Questions
Transient and Volatile Variables
Question 1: `transient` vs. `volatile`
In Java:
- `transient`: Prevents a variable from being serialized (its value isn't saved when the object is serialized).
- `volatile`: Ensures that all threads see the most up-to-date value of a variable. It prevents caching and reordering of variable access by multiple threads.
Feature | `transient` | `volatile` |
---|---|---|
Serialization | Not serialized | Serialized |
Thread Visibility | No special thread visibility | Ensures immediate visibility across threads |
Default Value (Deserialization) | Default value for the data type | No default value |
`static` Keyword | Cannot be used with `static` | Can be used with `static` |
Synchronized Methods and Thread Access
Question 2: Synchronized Methods and Thread Access
If a method is declared as `synchronized`, only one thread can access that method on a given object at a time. Other threads will wait until the lock is released. Non-synchronized methods can be accessed concurrently.
Java Code
class ABC {
synchronized void method1() {
System.out.println("In method - 1");
}
void method2() {
System.out.println("In method - 2");
}
}
In this example, `method1()` is synchronized, but `method2()` is not. Multiple threads could access `method2()` concurrently on the same object instance, but only one thread at a time could access `method1()`.
Varargs
Question 3: Varargs (...)
Varargs (variable arguments) allow a method to accept a variable number of arguments. The arguments are passed as an array.
Java Code
public void someMethod(String... infoArgs) {
for (String s : infoArgs) {
System.out.println(s);
}
}
someMethod("Java", "Interview", "Questions");
Output
Java
Interview
Questions
`ArrayList` vs. `Vector`
Question 4: `ArrayList` vs. `Vector`
Differences:
Feature | `ArrayList` | `Vector` |
---|---|---|
Synchronization | Not synchronized | Synchronized |
Thread Safety | Not thread-safe | Thread-safe |
Performance | Faster | Slower |
`equals()` and `hashCode()` Contract
Question 5: `equals()` and `hashCode()` Contract
If you override the `equals()` method in Java, you *must* also override the `hashCode()` method. The contract states that if two objects are equal according to `equals()`, their hash codes must also be equal. This is essential for using custom objects as keys in hash-based collections (like HashMap).
Floating-Point Arithmetic
Question 6: Floating-Point Arithmetic
Java Code
public class JTPProblem {
public static void main(String[] argvs) {
System.out.println(0.2 == 0.1 * 2); // Output: true
System.out.println(0.3 == 0.1 * 3); // Output: false
}
}
Floating-point numbers may not always be represented precisely in binary, leading to potential inaccuracies in comparisons. The result of floating point arithmetic might differ from the expected value.
`null` Values and Method Overloading
Question 8: `null` Values and Method Overloading
Java Code
public class SomeClass {
public static void meth1(Object o) {
System.out.println("Object method is getting Invoked."); // Output: String method is getting Invoked.
}
public static void meth1(String str) {
System.out.println("String method is getting Invoked.");
}
public static void main(String[] argvs) {
meth1(null);
}
}
In Java, `null` can be assigned to any object reference. The compiler chooses the most specific method signature when there's a match. In this case, `String` is more specific than `Object`, even though both methods could technically accept a `null` value.
`wait()` Method Invocation
Question 9: Invoking wait()
The `wait()` method should always be called within a loop to check the condition again when the thread becomes runnable again. This prevents spurious wakeups (where the thread wakes up without the condition being met).
Using `HashMap` in Multithreaded Environments
Question 10: Using HashMap in a Multithreaded Environment
While `HashMap` is not thread-safe, you can use it safely in a multithreaded context if only one thread modifies the map and other threads only read from it. For concurrent modifications, use `ConcurrentHashMap` or `HashTable`.
Immutable Objects
Question 12: Immutability and the `final` Keyword
It's a good practice to declare immutable objects as `final` in Java. This prevents modification after creation. However, declaring as `final` alone doesn't guarantee immutability; the class itself and its fields must be designed to prevent modifications.
Immutability and the `final` Keyword
Question 12: Immutability and the `final` Keyword
In Java, declaring a variable or object as `final` prevents reassignment to a new object reference. However, this doesn't automatically make an object immutable (its contents might still be changeable). To make a class immutable, you make all fields `private` and `final` and ensure that any mutable fields within the object cannot be modified after creation (e.g., by returning copies instead of direct references).
Factory Design Pattern
Question 13: Factory Design Pattern
The Factory design pattern is a creational pattern that provides a way to create objects without specifying their concrete classes. This is usually done through an interface or abstract class, allowing subclasses to decide which object to instantiate. The factory design pattern hides the object creation logic from the client.
String Creation: Literal vs. `new`
Question 14: String Creation: Literal vs. `new`
Differences:
- String Literal: Creates a String object in the String pool (a special memory area in the heap). Multiple string literals with the same value refer to the same object.
- `new String()` : Creates a new String object in the heap (not the String pool). Two String objects with the same value will be distinct objects with different memory locations.
Floating-Point Comparison
Question 15: Floating-Point Comparison
Java Code
public class JTPProblem {
public static void main(String[] argvs) {
System.out.println(0.0d == Double.MIN_VALUE); // Output: false
System.out.println(Math.min(0.0d, Double.MIN_VALUE)); // Output: -4.9E-324
}
}
Due to limitations in representing floating-point numbers in binary format, direct comparisons of floating-point values can lead to unexpected results. It is preferable to check if two floating-point numbers are close to each other using a tolerance rather than checking for exact equality. In this case, `Math.min()` returns the smaller of the two values, and `Double.MIN_VALUE` is a small positive number.
Java 8 Features
Question 16: Important Features of Java 8
Key Java 8 features include:
- Lambda expressions.
- Streams API.
- Default methods in interfaces.
- Optional class.
- Date and time API improvements.
Double-Checked Locking
Question 17: Double-Checked Locking in Singleton Pattern
Double-checked locking is a thread-safe way to create singleton objects. It involves checking for an existing instance twice—once without synchronization and then within a synchronized block—to enhance efficiency. This prevents multiple instances of the Singleton from being created.
Java Code
public class Singleton {
private static Singleton instance = null;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
Dependency Injection
Question 18: Dependency Injection
Dependency Injection (DI) is a design pattern that promotes loose coupling by providing dependencies to a class from outside rather than creating them within the class itself. Frameworks like Spring utilize DI extensively.
Integer Comparison
Question 19: Integer Comparison in Java
Java Code
public class JTPProblem {
public static void main(String[] argvs) {
Integer n1 = 2000, n2 = 2000;
System.out.println(n1 == n2); // Output: false
Integer n3 = 10, n4 = 10;
System.out.println(n3 == n4); // Output: true
}
}
The `Integer` class caches values from -128 to 127. For numbers within this range, `==` will return `true` because both references point to the same cached object; for numbers outside this range, `==` compares references, which are different, resulting in `false`.
Overloading `main()`
Question 20: Overloading the `main()` Method
Yes, you can overload the `main()` method in Java. However, only the method with the signature `public static void main(String[] args)` will be executed by the JVM.
Enumeration vs. Iterator
Question 21: Enumeration vs. Iterator
Key differences:
Feature | Enumeration | Iterator |
---|---|---|
Methods | `hasMoreElements()`, `nextElement()` | `hasNext()`, `next()`, `remove()` |
`remove()` Method | Not supported | Supported |
Usage | Legacy; used with older collections | More modern; used with all collections |
`EnumSet`
Question 22: EnumSet
EnumSet
is a specialized Set
implementation in Java designed specifically for working with enum types. It's highly efficient and compact because it's internally represented as a bit vector. This makes it faster than general-purpose Set
implementations like HashSet
or TreeSet
when dealing exclusively with enums.
Advantages of EnumSet:
- Performance:
EnumSet
operations (add, remove, contains, etc.) are very fast due to the bit vector implementation. - Compactness: It uses minimal memory, especially when compared to other
Set
implementations. - Type Safety:
EnumSet
is type-safe; it only accepts elements of the specified enum type.
When to Use EnumSet:
- When you need a
Set
containing only enum values. - When performance is critical, and you're working with enums.
- When memory usage is a concern.
Immutability and `final`
Question 12: Immutability and the `final` Keyword
Declaring a variable `final` in Java prevents reassignment of the variable to a new object. However, this doesn't make the object itself immutable if the object's internal state can be changed. To create a truly immutable object, you make the class itself `final`, make all fields `private` and `final`, and don't provide any methods that modify the object's internal state.
Factory Design Pattern
Question 13: Factory Design Pattern
The Factory design pattern is a creational pattern that provides an interface for creating objects without specifying their concrete classes. This is often done through a factory method, enhancing flexibility and enabling the creation of different objects from a single interface. The client doesn't need to know the specific classes being created.
String Creation Methods
Question 14: String Creation: Literal vs. `new`
Creating Strings in Java:
- Literal:
String str = "hello";
(Uses the String pool; multiple literals with the same value point to the same object). - `new String()` :
String str = new String("hello");
(Creates a new object in the heap; the literal also exists in the String pool.)
Floating-Point Number Comparison
Question 15: Floating-Point Number Comparison
Java Code
public class JTPProblem {
public static void main(String[] argvs) {
System.out.println(Math.min(0.0d, Double.MIN_VALUE)); // Output: -4.9E-324
}
}
Direct comparison of floating-point numbers for equality might give unexpected results due to precision limitations. The smallest positive `double` value is `Double.MIN_VALUE`, which is a small positive number, hence `Math.min(0.0d, Double.MIN_VALUE)` returns `-4.9E-324`.
Java 8 Features
Question 16: Important Features of Java 8
Key features introduced in Java 8:
- Lambda Expressions: Anonymous functions; enable functional programming.
- Streams API: Provides a fluent API for processing collections of data.
- Default Methods in Interfaces: Allows adding new methods to interfaces without breaking existing implementations.
- Optional Class: Handles situations where a value might be absent.
- Date and Time API: Improved and more robust API for handling dates and times.
Double-Checked Locking
Question 17: Double-Checked Locking
Double-checked locking is a thread-safe way to create singleton objects. It optimizes instantiation by checking for an instance twice—once without synchronization and then within a synchronized block.
Java Code
public class Singleton {
private static Singleton instance = null;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
Dependency Injection
Question 18: Dependency Injection
Dependency injection is a design pattern that promotes loose coupling by providing dependencies to a class from the outside (typically a framework like Spring) rather than creating them within the class. This improves code flexibility and testability.
Overloading the `main` Method
Question 20: Overloading the `main` Method
Yes, you can overload the `main` method in Java. However, only the method with the signature `public static void main(String[] args)` is executed by the JVM.
`EnumSet`
Question 22: EnumSet
(This would require a fuller answer explaining `EnumSet`, which is a specialized Set implementation for enum types that offers optimized performance and memory efficiency.)
`ArrayList` vs. `LinkedList`
Question 20: `ArrayList` vs. `LinkedList`
Differences:
Feature | `ArrayList` | `LinkedList` |
---|---|---|
Underlying Data Structure | Dynamic array | Doubly linked list |
Data Access | Fast random access (using index) | Slower random access |
Insertion/Deletion | Slower (requires shifting elements) | Faster (no element shifting) |
`HashTable` vs. `HashMap`
Question 18: `HashTable` vs. `HashMap`
Both implement the `Map` interface, but `HashMap` is not synchronized (not thread-safe), while `HashTable` is synchronized (thread-safe). `HashMap` offers better performance in single-threaded environments.