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.