Blog

Parallel Sum Calculation Using Multithreading in Java

Let’s simplify the concept with a basic example of multithreading: calculating the sum of an array of numbers. In this scenario, we’ll split an array into two halves and sum each half in a separate thread. Finally, we’ll combine the sums from both threads to get the total sum. This is a simple demonstration of how to use multithreading to divide a task into smaller parts and execute them in parallel.

Here’s how you can do it in Java:

public class SumArrayWithThreads {
    // Inner class to represent a task that calculates the sum of part of an array
    static class SumTask implements Runnable {
        private int[] array;
        private int start, end;
        private long sum = 0;

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

        @Override
        public void run() {
            for (int i = start; i < end; i++) {
                sum += array[i];
            }
        }

        public long getSum() {
            return sum;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        // Example array
        int[] numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        int mid = numbers.length / 2;

        // Create tasks to sum each half of the array
        SumTask sumTask1 = new SumTask(numbers, 0, mid);
        SumTask sumTask2 = new SumTask(numbers, mid, numbers.length);

        // Create threads for each task
        Thread thread1 = new Thread(sumTask1);
        Thread thread2 = new Thread(sumTask2);

        // Start the threads
        thread1.start();
        thread2.start();

        // Wait for the threads to finish
        thread1.join();
        thread2.join();

        // Combine the results
        long totalSum = sumTask1.getSum() + sumTask2.getSum();

        System.out.println("Total sum: " + totalSum);
    }
}

In this example, the SumTask class implements the Runnable interface, allowing it to be executed by a thread. It calculates the sum of the elements in its assigned part of the array. We create two instances of SumTask, each responsible for summing half of the array. Then, we create two threads, each executing one of the SumTask instances. After both threads have completed their execution (indicated by the join() calls), we combine their results to get the total sum of the array.

This example demonstrates how to use multithreading to perform number operations in parallel, which can be especially beneficial for larger datasets or more computationally intensive operations.

Each thread calculates the sum of its segment. After all threads have completed their execution, we aggregate their results to get the total sum.

This approach allows the program to utilize multiple processor cores, thereby reducing the overall computation time compared to a single-threaded approach, especially for large arrays and complex operations.

But thread is associated with Single Process how it can utilize the multiple processor?

The ability of threads within a single process to utilize multiple processors (or cores) is a key feature of modern operating systems and their scheduling mechanisms. When a process spawns multiple threads, the operating system’s scheduler has the capability to distribute these threads across the available processors or cores for concurrent execution. This is how multithreading can leverage the power of multi-core processors to achieve parallelism within a single process. Here’s how it works:

Operating System Scheduler

The operating system’s scheduler is responsible for managing all running processes and threads on a system. It allocates CPU time to processes and threads, deciding which ones should run, when, and on which core. Modern schedulers are designed to take advantage of multi-core processors by distributing threads across different cores.

Thread-Level Parallelism

When a process creates multiple threads, each thread can potentially run on a separate core, allowing for true parallel execution of tasks. This is known as thread-level parallelism. The scheduler makes decisions based on various factors including load balancing (distributing work evenly across cores), thread affinity (preference of a thread to run on a specific core), and other system-level considerations.

Multi-core Processors

Multi-core processors have multiple processing units (cores) within a single physical package. Each core can execute threads independently. This architecture allows for multiple threads to be executed in parallel, significantly improving performance for multi-threaded applications.

Benefits of Multithreading on Multi-core Processors

  1. Improved Performance: By distributing different threads of a single process across multiple cores, a multi-threaded application can perform more tasks in the same amount of time compared to running sequentially on a single core.
  2. Efficient Resource Utilization: Multi-threading allows an application to make efficient use of the available CPU resources by keeping idle time to a minimum and utilizing multiple cores.
  3. Concurrency: Multithreading enables applications to achieve concurrency, where multiple threads make progress simultaneously, leading to faster completion of tasks and more responsive applications.

Example

Consider a web server handling multiple incoming connections. Each connection can be processed by a separate thread. On a multi-core processor, these threads can run on different cores simultaneously, allowing the server to handle multiple requests in parallel. This parallel processing improves the server’s throughput and its ability to serve more clients in less time.

In conclusion, the combination of multi-threaded programming and multi-core processors allows applications to achieve high levels of parallelism and performance. The operating system’s scheduler plays a crucial role in enabling this by distributing threads across available cores, thereby harnessing the full power of the hardware.

Avatar

Neelabh

About Author

As Neelabh Singh, I am a Senior Software Engineer with 6.6 years of experience, specializing in Java technologies, Microservices, AWS, Algorithms, and Data Structures. I am also a technology blogger and an active participant in several online coding communities.

You may also like

Blog Design Pattern

Understanding the Builder Design Pattern in Java | Creational Design Patterns | CodeTechSummit

Overview The Builder design pattern is a creational pattern used to construct a complex object step by step. It separates
Blog Tech Toolkit

Base64 Decode

Base64 encoding is a technique used to encode binary data into ASCII characters, making it easier to transmit data over