Multithreading in Java:

Introduction of Multithreading in java

Multithreading is a powerful feature in Java that enables efficient CPU usage by allowing multiple threads to run simultaneously. By learning multithreading in Java, developers can optimize code performance and create responsive applications.

What is Multithreading in Java?

Multithreading refers to the ability of a CPU to execute multiple threads concurrently. A thread is the smallest unit of a process, and multithreading allows these units to run independently.

Benefits of Multithreading:

  • Improved application performance.
  • Better resource utilization.
  • Enhanced responsiveness, especially for applications with complex tasks.
Core Concepts of Multithreading

Thread Lifecycle:

  • New: The thread is created.
  • Runnable: The thread is ready to run.
  • Blocked: The thread is blocked and waiting.
  • Terminated: The thread has completed execution.

Code Example: Basic Multithreading Structure

public class MyThread extends Thread {
    public void run() {
        System.out.println("Thread is running.");
    }

    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start(); // Starts the thread
    }
}
How to Create Threads in Java

Java provides two main ways to implement threads: by extending the Thread class or implementing the Runnable interface.

Code Example: Using Runnable Interface

 MyRunnable implements Runnable {
    public void run() {
        System.out.println("Runnable thread is running.");
    }

    public static void main(String[] args) {
        Thread thread = new Thread(new MyRunnable());
        thread.start();
    }
}
Thread Synchronization in Java

In multithreading, synchronization ensures that two threads do not simultaneously access shared resources, preventing inconsistent data.

Code Example: Synchronized Block

codeclass Counter {
    private int count = 0;

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

    public int getCount() {
        return count;
    }
}

class SyncExample {
    public static void main(String[] args) {
        Counter counter = new Counter();

        // Multiple threads incrementing the counter
        Thread t1 = new Thread(counter::increment);
        Thread t2 = new Thread(counter::increment);

        t1.start();
        t2.start();
    }
}
Best Practices for Multithreading in Java
  • Minimize Synchronization: Overusing synchronization can cause performance bottlenecks.
  • Use Thread Pools: Instead of creating new threads for each task, use a thread pool to manage resources efficiently.
  • Avoid Deadlocks: Carefully structure code to prevent threads from blocking each other indefinitely.

Code Example: Using Thread Pools

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolExample {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(5);
        
        for (int i = 0; i < 10; i++) {
            executor.submit(() -> System.out.println("Executing task"));
        }
        
        executor.shutdown();
    }
}

Common Interview Questions

1. What is the difference between Thread and Runnable in Java?
  • Asked by: Amazon, Cognizant, IBM
  • Answer: Thread is a class that represents a thread of execution, while Runnable is an interface for defining tasks that can be executed by a thread. Implementing Runnable allows for better design and flexibility because it separates the task from the executor. With Runnable, you can also extend other classes, as Java doesn’t support multiple inheritance.

2. How do you prevent thread interference in Java?
  • Asked by: Oracle, TCS, Wipro
  • Answer: Thread interference occurs when multiple threads concurrently modify shared data without proper synchronization. To prevent this, use:
    • Synchronized methods or blocks to allow only one thread at a time to access critical sections.
    • java.util.concurrent locks and atomic variables (e.g., AtomicInteger) to manage access safely.
    • Volatile keyword to ensure changes to variables are visible to all threads immediately.
    • Thread-safe collections like ConcurrentHashMap for shared data structures.

3. Explain deadlock and how to avoid it in Java.
  • Asked by: Microsoft, Infosys, SAP
  • Answer: Deadlock occurs when two or more threads are waiting on each other’s resources, causing an indefinite block. To avoid deadlock:
    • Use a consistent order for locking resources across threads.
    • Use timeout-based locking with tryLock to avoid indefinite waiting.
    • Limit the scope of locks by avoiding nested locks and using higher-level constructs like semaphores, or synchronized collections that manage locks internally.

4. What are daemon threads, and how do they differ from regular threads?
  • Asked by: Adobe, Google, Cisco
  • Answer: Daemon threads are background threads that support user threads and run continuously. Unlike user threads, the JVM does not wait for daemon threads to finish before exiting, so they’re suited for tasks like garbage collection and monitoring. You can set a thread as daemon by using setDaemon(true).

5. What is the purpose of the join() method in Java, and when would you use it?
  • Asked by: Accenture, LinkedIn, Facebook
  • Answer: The join() method makes one thread wait for another to complete. For example, if Thread A calls threadB.join(), Thread A will pause execution until Thread B finishes. This is useful when a thread’s task depends on another thread’s completion, or when you need to maintain a specific sequence of task executions.

Practice Exercises

  • Create a program that uses threads to perform multiple calculations.
  • Implement a synchronized counter that can be accessed by multiple threads.

Additional Resources

PLAY WITH JAVA..!!