package jvm; import java.lang.System; // Import System for nanoTime and printf import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; /** * A Java class to replicate the calculation performed by the Python script, * optimized using multithreading. */ public class Calculation2 { /** * Represents a task that calculates a partial sum for a given range of iterations. * Implements Callable so it can be executed by an ExecutorService and return a result. */ private static class PartialCalculator implements Callable { private final int startIteration; private final int endIteration; private final int param1; private final int param2; /** * Constructor for the partial calculator task. * @param startIteration The starting iteration (inclusive). * @param endIteration The ending iteration (exclusive). * @param param1 The first parameter for calculation. * @param param2 The second parameter for calculation. */ public PartialCalculator(int startIteration, int endIteration, int param1, int param2) { this.startIteration = startIteration; this.endIteration = endIteration; this.param1 = param1; this.param2 = param2; } /** * The computation performed by this task. Calculates the sum for the assigned range. * @return The partial sum for the range. */ @Override public Double call() { double partialSum = 0.0; // Loop through the assigned range of iterations // Note: loop goes up to < endIteration for (int i = startIteration; i < endIteration; i++) { // Calculate the first value of j double jMinus = (double)i * param1 - param2; // Subtract 1.0 / jMinus from the partial sum partialSum -= (1.0 / jMinus); // Calculate the second value of j double jPlus = (double)i * param1 + param2; // Add 1.0 / jPlus to the partial sum partialSum += (1.0 / jPlus); } return partialSum; } } /** * Performs the iterative calculation using multiple threads. * * @param iterations The total number of iterations for the loop. * @param param1 The first parameter used in the calculation. * @param param2 The second parameter used in the calculation. * @param numThreads The number of threads to use for the calculation. * @return The result of the calculation. * @throws InterruptedException If thread execution is interrupted. * @throws ExecutionException If computation threw an exception. */ public static double calculateParallel(int iterations, int param1, int param2, int numThreads) throws InterruptedException, ExecutionException { // Create a fixed-size thread pool ExecutorService executor = Executors.newFixedThreadPool(numThreads); // List to hold the Future objects representing the results of each task List> futureResults = new ArrayList<>(); // Calculate the approximate number of iterations per thread int iterationsPerThread = iterations / numThreads; int start = 1; // Start iteration from 1 // Divide the work and submit tasks to the executor for (int i = 0; i < numThreads; i++) { int end = start + iterationsPerThread; // For the last thread, ensure it covers all remaining iterations if (i == numThreads - 1) { end = iterations + 1; // Go up to iterations (inclusive) } // Ensure end doesn't exceed the total iterations + 1 boundary if (end > iterations + 1) { end = iterations + 1; } // Create and submit the task for the calculated range // The loop inside PartialCalculator runs from start (inclusive) to end (exclusive) Callable task = new PartialCalculator(start, end, param1, param2); futureResults.add(executor.submit(task)); // Set the start for the next chunk start = end; } // Initialize the total result (starting from the base 1.0) double totalResult = 1.0; // Retrieve results from each Future and add to the total result for (Future future : futureResults) { // future.get() blocks until the result is available totalResult += future.get(); } // Shut down the executor service gracefully executor.shutdown(); // Return the final combined result return totalResult; } /** * The main entry point of the program. * * @param args Command line arguments (not used). */ public static void main(String[] args) { // Define the parameters for the calculation final int ITERATIONS = 100_000_000; final int PARAM1 = 4; final int PARAM2 = 1; // Determine the number of threads based on available processors final int NUM_THREADS = Runtime.getRuntime().availableProcessors(); System.out.println("Using " + NUM_THREADS + " threads."); // Record the start time long startTime = System.nanoTime(); double finalResult = 0; try { // Perform the parallel calculation and multiply the result by 4.0 finalResult = calculateParallel(ITERATIONS, PARAM1, PARAM2, NUM_THREADS) * 4.0; // Record the end time long endTime = System.nanoTime(); // Calculate the duration long durationNanos = endTime - startTime; double durationSeconds = durationNanos / 1_000_000_000.0; // Print the final result and execution time System.out.printf("Result: %.12f%n", finalResult); System.out.printf("Execution Time: %.6f seconds%n", durationSeconds); } catch (InterruptedException | ExecutionException e) { // Handle potential exceptions during parallel execution System.err.println("Calculation failed: " + e.getMessage()); e.printStackTrace(); // Ensure System.exit is not used in production code without careful consideration // System.exit(1); } } }