Monday, June 26, 2023

How to use Exchanger for Inter thread communication in Java? Example Tutorial

In Java, you can use the Exchanger class from the java.util.concurrent package to facilitate communication between two threads. The Exchanger provides a synchronization point where two threads can exchange objects. Each thread waits at the exchange() method until both threads have reached it, and then they swap their objects. 

 Here's an example that demonstrates how to use the Exchanger class for inter-thread communication:


import java.util.concurrent.Exchanger;

class FirstThread extends Thread {
    private Exchanger exchanger;

    public FirstThread(Exchanger exchanger) {
        this.exchanger = exchanger;
    }

    public void run() {
        try {
            // Sending a message to the second thread
            String message = "Hello from the first thread!";
            System.out.println("First thread sends: " + message);
            exchanger.exchange(message);

            // Receiving a message from the second thread
            String receivedMessage = exchanger.exchange(null);
            System.out.println("First thread receives: " + receivedMessage);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class SecondThread extends Thread {
    private Exchanger exchanger;

    public SecondThread(Exchanger exchanger) {
        this.exchanger = exchanger;
    }

    public void run() {
        try {
            // Receiving a message from the first thread
            String receivedMessage = exchanger.exchange(null);
            System.out.println("Second thread receives: " + receivedMessage);

            // Sending a message to the first thread
            String message = "Hello from the second thread!";
            System.out.println("Second thread sends: " + message);
            exchanger.exchange(message);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public class ExchangerExample {
    public static void main(String[] args) {
        Exchanger exchanger = new Exchanger<>();

        FirstThread firstThread = new FirstThread(exchanger);
        SecondThread secondThread = new SecondThread(exchanger);

        firstThread.start();
        secondThread.start();

        try {
            firstThread.join();
            secondThread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

In this example, we have two threads: FirstThread and SecondThread. Both threads share a common Exchanger object. 

The FirstThread sends a message to the SecondThread by calling exchanger.exchange(message), where message is the object to be exchanged. The thread then waits until the SecondThread also reaches the exchange() method. Once both threads have reached the exchange point, they swap their objects. 

Similarly, the SecondThread receives the message sent by the FirstThread by calling exchanger.exchange(null), and then sends its own message by calling exchanger.exchange(message). 

When you run this example, the output might look like this:


First thread sends: Hello from the first thread!
Second thread receives: Hello from the first thread!
Second thread sends: Hello from the second thread!
First thread receives: Hello from the second thread!

As you can see, the messages are exchanged between the two threads using the Exchanger, allowing them to communicate with each other.

Friday, June 23, 2023

Difference between Process and Thread in Java - Example

In Java, a process and a thread are both units of execution, but they differ in their characteristics and functionality. Let's explore the difference between a process and a thread with an example:

Process:

A process can be thought of as an instance of a running program. It has its own memory space and resources. Each process runs independently and does not directly share memory with other processes. Processes are managed by the operating system, and inter-process communication (IPC) mechanisms like pipes or sockets are typically used for communication between processes.

Example: Consider a scenario where you have a text editor application and a web browser application running simultaneously on your computer. These two applications are separate processes. If one of them crashes, it does not affect the other process.

Thread:

A thread is a lightweight unit of execution within a process. Multiple threads can exist within a single process, and they share the same memory space and resources of that process. Threads are used to achieve parallelism or concurrent execution within a program. They allow multiple tasks to be executed concurrently, enhancing performance and responsiveness.

Example: Imagine a music player application where you have a user interface that displays the current song information and a background thread that continuously buffers and plays the audio. The user interface and audio playback are separate threads within the same process. The user can interact with the interface while the audio plays uninterrupted.


Key Differences:


Memory and Resources: Each process has its own memory space and resources, while threads share the same memory and resources within a process.

Communication: Processes typically use IPC mechanisms for communication, while threads communicate through shared memory within a process.

Independence: Processes are independent entities, and one process crashing does not affect others. Threads within a process are interdependent, and issues in one thread can impact the entire process.

Creation Overhead: Creating a new process is more resource-intensive as it requires duplicating the entire process, including its memory space. Creating a thread is relatively lightweight and has less overhead.

Scheduling: The operating system schedules processes, allocating CPU time to each process independently. Threads within a process share the CPU time allocated to that process.


It's important to note that Java provides built-in support for threads through the Thread class and related APIs. Processes, on the other hand, are managed by the operating system rather than the Java language itself.






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.