Wednesday, June 21, 2023

How to use Fork Join in Java Multithreading - Tutorial with Example

Fork-Join is a framework in Java that allows you to perform parallel processing by dividing a task into smaller subtasks and merging the results. It is part of the java.util.concurrent package and is useful for efficiently utilizing multiple CPU cores for concurrent processing. Here's a step-by-step guide on how to use Fork-Join in Java:


Step 1: Create the Fork-Join task


Extend the RecursiveTask class if your task returns a result, or extend the RecursiveAction class if your task does not return a result.

Override the compute() method, which represents the main computation performed by the task.

Break down the task into smaller subtasks and delegate them to other instances of the same task.

Combine the results of the subtasks to obtain the final result (if applicable).


Here's an example of a RecursiveTask that computes the sum of an array of integers:


import java.util.concurrent.RecursiveTask;

public class SumTask extends RecursiveTask {
    private static final int THRESHOLD = 1000;
    private int[] array;
    private int start;
    private int end;

    public SumTask(int[] array, int start, int end) {
        this.array = array;
        this.start = start;
        this.end = end;
    }

    @Override
    protected Integer compute() {
        if (end - start <= THRESHOLD) {
            int sum = 0;
            for (int i = start; i < end; i++) {
                sum += array[i];
            }
            return sum;
        } else {
            int mid = (start + end) / 2;
            SumTask leftTask = new SumTask(array, start, mid);
            SumTask rightTask = new SumTask(array, mid, end);
            leftTask.fork(); // Start the left subtask asynchronously
            int rightResult = rightTask.compute(); // Compute the right subtask synchronously
            int leftResult = leftTask.join(); // Wait for the left subtask to complete and get its result
            return leftResult + rightResult;
        }
    }
}

Step 2: Create the Fork-Join pool and submit the task 

Create an instance of the ForkJoinPool class, which manages the execution of Fork-Join tasks. 

Create an instance of your Fork-Join task. Submit the task to the Fork-Join pool using the invoke() or submit() method. 


Here's an example of how to use the SumTask in the main method:


import java.util.concurrent.ForkJoinPool;

public class Main {
    public static void main(String[] args) {
        int[] array = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

        ForkJoinPool forkJoinPool = new ForkJoinPool();
        SumTask task = new SumTask(array, 0, array.length);
        int result = forkJoinPool.invoke(task);

        System.out.println("Sum: " + result);
    }
}

In this example, we create a Fork-Join pool, create an instance of SumTask, and then invoke the task using the invoke() method of the pool. 

The result is obtained and printed to the console. By breaking down the task into smaller subtasks and using the Fork-Join framework, you can take advantage of parallel processing and improve the performance of your Java multithreaded applications.

Tuesday, June 20, 2023

4 Reasons and Benefits of Using Multithreading in Java? Why Threads?

Using multithreading in Java offers several reasons and benefits, including:


Improved performance and responsiveness: Multithreading allows concurrent execution of multiple threads within a single program. By dividing tasks into smaller threads and executing them simultaneously, you can take advantage of available CPU cores and increase overall program performance. It enables better utilization of system resources and improves the responsiveness of applications, especially in scenarios where tasks can be executed in parallel.


Enhanced concurrency: Concurrency refers to the ability to execute multiple tasks concurrently. Multithreading enables concurrent execution by allowing different threads to execute independently, sharing the CPU time. This is particularly beneficial in situations where you need to handle multiple requests or perform multiple operations simultaneously, such as serving multiple clients in a server application or processing multiple tasks concurrently.


Efficient resource utilization: Multithreading helps in efficient utilization of system resources. By leveraging multiple threads, you can perform computations, I/O operations, or other tasks concurrently. This enables better utilization of CPU time, reduces idle time, and avoids resource wastage. For example, you can use separate threads for performing time-consuming operations like file I/O or network requests, while the main thread continues with other tasks.


Simplified program structure: Multithreading allows you to structure your program in a more intuitive and modular way. By separating different tasks or components into separate threads, you can manage and coordinate them independently. This can result in cleaner and more maintainable code, as well as improved code reusability. Multithreading also enables you to build more complex and interactive applications, such as user interfaces that remain responsive while performing background computations.


Overall, using threads in Java provides benefits like improved performance, enhanced concurrency, efficient resource utilization, and simplified program structure. However, it's important to note that multithreading introduces additional complexity, such as potential thread synchronization issues and increased debugging challenges. Proper thread management, synchronization mechanisms, and consideration of thread safety are crucial to ensure correct and reliable multithreaded applications.

Monday, June 19, 2023

What is Daemon thread in Java and Difference to Non daemon thread - Tutorial Example

In Java, a thread is a lightweight unit of execution that allows concurrent processing within a program. Threads can be classified as daemon threads or non-daemon threads, depending on their behavior. Let me explain the difference between them.


1. Daemon Threads:

A daemon thread is a type of thread that runs in the background and does not prevent the Java Virtual Machine (JVM) from exiting when the main thread completes. In other words, a daemon thread is a low-priority thread that runs in the background to perform tasks that support the main application threads.


Characteristics of daemon threads:

Daemon threads are created using the setDaemon(true) method on a Thread object before starting it.

The JVM automatically terminates all daemon threads when there are no more non-daemon threads running.

Daemon threads should not perform critical operations or hold resources that need to be properly released since they may be abruptly terminated by the JVM.

Typical examples of daemon threads include garbage collection, background logging, or other maintenance tasks.

Here's an example that demonstrates a daemon thread in Java:


public class DaemonThreadExample {
    public static void main(String[] args) {
        Thread daemonThread = new Thread(() -> {
            while (true) {
                System.out.println("Daemon thread is running.");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        daemonThread.setDaemon(true); // Set the thread as daemon
        daemonThread.start(); // Start the thread

        System.out.println("Main thread is finished.");
    }
}

In the example, we create a daemon thread that continuously prints a message to the console. The main thread completes its execution, but the daemon thread keeps running in the background. If you run this code, you'll see the "Daemon thread is running." message printed repeatedly until you terminate the program. Non-Daemon Threads: 

2. A non-daemon thread, also known as a user thread, is the opposite of a daemon thread. Non-daemon threads are designed to perform critical tasks and prevent the JVM from exiting as long as they are running. The JVM waits for all non-daemon threads to complete before terminating the program. 


Here's an example that demonstrates a non-daemon thread:


public class NonDaemonThreadExample {
    public static void main(String[] args) {
        Thread nonDaemonThread = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                System.out.println("Non-daemon thread is running.");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        nonDaemonThread.setDaemon(false); // Set the thread as non-daemon (optional, since it's the default)
        nonDaemonThread.start(); // Start the thread

        System.out.println("Main thread is finished.");
    }
}

In this example, we create a non-daemon thread that prints a message to the console five times. The main thread waits for the non-daemon thread to complete its execution before terminating. If you run this code, you'll see both the non-daemon thread and the main thread messages printed alternately. 

Remember, if all the non-daemon threads complete their execution, the JVM will exit, even if there are daemon threads still running.

Saturday, June 17, 2023

Difference between Thread vs Runnable interface in Java

 In Java, both the Thread class and the Runnable interface are used for creating and managing concurrent threads of execution. They serve similar purposes but differ in their implementation approach. Here are the key differences between the two:


Inheritance vs Interface: The Thread class is a concrete class that extends the java.lang.Thread class, making it capable of directly creating and managing threads. On the other hand, the Runnable interface is implemented by a class, and the class can be used to create a Thread object using the Runnable instance.


Extending vs Implementing: To create a thread using the Thread class, you need to extend it and override its run() method. This allows you to define the code that will be executed in the thread. In contrast, to use the Runnable interface, you need to implement the run() method in a separate class. The run() method contains the code that will be executed when the thread is started.


Reusability: The use of Runnable interface provides better reusability than extending the Thread class. With Runnable, you can implement the interface in multiple classes and create threads from different instances of those classes. This promotes a more flexible and modular design by separating the task logic from the thread management.


Single Inheritance Constraint: Java allows a class to extend only one class, which means if you extend the Thread class, you cannot extend any other class. However, by implementing the Runnable interface, you can still extend another class and implement Runnable, as Java supports multiple interfaces.


Encapsulation: Implementing Runnable separates the task (defined in the run() method) from the thread's behavior, allowing better encapsulation. It enables you to pass the Runnable instance to different thread constructors, promoting code reuse and modularity.


Resource Sharing: When multiple threads need to share resources or data, implementing Runnable is generally preferred. By passing the same instance of the Runnable implementation to multiple threads, they can access and manipulate shared resources easily. In contrast, extending the Thread class may lead to limitations in resource sharing.


In summary, the Thread class provides a convenient way to create and manage threads, while the Runnable interface offers a more flexible and reusable approach to defining thread behavior. The choice between the two depends on the specific requirements of your application and the design principles you want to follow.






Friday, June 16, 2023

What is Timer and TimerTask in Java – Tutorial Example

In Java, the Timer and TimerTask classes are used for scheduling tasks to be executed at a specified time or after a specific interval. These classes provide a convenient way to perform time-based operations in Java applications. This tutorial will introduce you to the Timer and TimerTask classes and demonstrate their usage through an example.


Timer Class

The Timer class in Java provides a facility for scheduling tasks to be executed at a specified time or after a certain delay. It is part of the java.util package and was introduced in JDK 1.3. The Timer class internally uses a single background thread to execute scheduled tasks.


To use the Timer class, you need to create an instance of it and schedule tasks using its schedule() or scheduleAtFixedRate() methods. The schedule() method is used to schedule a task to be executed once, while the scheduleAtFixedRate() method is used to schedule a task to be executed repeatedly at fixed intervals.


TimerTask Class

The TimerTask class is an abstract class that represents a task to be scheduled by a Timer. To use the TimerTask class, you need to create a subclass and override its run() method. The run() method contains the code that will be executed when the task is triggered.


Example: Scheduling a Task

Let's see an example that demonstrates how to use the Timer and TimerTask classes to schedule a task in Java:


import java.util.Timer;
import java.util.TimerTask;

public class TaskScheduler {
    public static void main(String[] args) {
        TimerTask task = new TimerTask() {
            @Override
            public void run() {
                System.out.println("Task executed!");
            }
        };

        Timer timer = new Timer();
        timer.schedule(task, 5000); // Schedule the task to be executed after 5 seconds
    }
}

In the above example, we create a TimerTask subclass by overriding its run() method, which simply prints "Task executed!" to the console. We then create an instance of the Timer class and schedule the task using the schedule() method, specifying a delay of 5000 milliseconds (5 seconds). 

When you run this program, it will wait for 5 seconds and then execute the task, printing "Task executed!" to the console. 

Cancelling a Task 

If you want to cancel a scheduled task before it is executed, you can use the Timer class's cancel() method. 

Here's an example that demonstrates task cancellation:


import java.util.Timer;
import java.util.TimerTask;

public class TaskScheduler {
    public static void main(String[] args) {
        TimerTask task = new TimerTask() {
            @Override
            public void run() {
                System.out.println("Task executed!");
            }
        };

        Timer timer = new Timer();
        timer.schedule(task, 5000); // Schedule the task to be executed after 5 seconds

        // Cancel the task after 3 seconds
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                task.cancel();
                System.out.println("Task cancelled!");
            }
        }, 3000);
    }
}

In this example, we schedule a task to be executed after 5 seconds, but we also schedule another task to cancel the first task after 3 seconds. When you run this program, you will see that "Task cancelled!" is printed to the console before "Task executed!" because the cancellation task runs earlier. 

Conclusion 

The Timer and TimerTask classes in Java provide a convenient way to schedule tasks to be executed at specific times or after certain intervals. By using these classes, you can