import java.util.Arrays; // for testing purposes only import java.util.Scanner; // for testing purposes only /* This class has a method that computes the sum of the elements of a given ** array of integers. It uses threads so that the work is done (potentially, ** at least) in parallel. ** ** Author: R. McCloskey ** Date: November 18, 2020 ** Updated: May 10, 2021 */ public class ArraySummerParallel { // class constant // -------------- private static final int LENGTH_THRESHOLD = 7; // instance variable // ----------------- private int[] a; // Array whose elements were "summed" // Note: The reason for the above being an instance variable of this // class is so that (the many) instances of the nested class can access // it without each one needing its own instance variable referring to it. /* Returns the sum of the elements in the specified segment of the given ** array (ary[low..high)). The result is computed using a thread based ** upon an instance of the nested class. */ public int sumOf(int[] ary, int low, int high) { this.a = ary; SumInParallel summer = new SumInParallel(low, high); Thread t = new Thread(summer); t.start(); join(t); return summer.sum; } /* Returns the sum of the elements in the given array. The result is ** computed using a thread based upon an instance of the nested class. */ public int sumOf(int[] ary) { return sumOf(ary, 0, ary.length); } /* An instance of this nested class is intended to be used in constructing ** a thread that computes the sum of the elements in a segment of a[], ** where the boundaries of that segment are provided as parameters to the ** constructor. */ private class SumInParallel implements Runnable { // instance variables // ------------------ int low, high; // Calling run() on this object results in the int sum; // sum of the elements in a[low..high) being stored // in instance variable 'sum'. // constructor // ----------- /* Identifies the array segment to be summed by this object. ** pre: 0 <= floor <= ceiling <= a.length */ public SumInParallel(int floor, int ceiling) { low = floor; high = ceiling; } // run() method // ------------ public void run() { //computeSum(); sum = computeSum(low, high); } // mutator // ------- /* Computes the sum of the elements in a[low..high) and places the ** result in instance variable 'sum'. */ private void computeSum() { sum = computeSum(low, high); } /* Returns the sum of the elements in a[left..right). ** How: If the given segment is sufficiently short, it computes the sum ** sequentially (using a call to sumOfSeq()). ** It the segment is longer, it sorts the left half using a new instance ** of this class (SumInParallel) in a newly-created thread. The right ** half is sorted using a recursive call to this method. */ private int computeSum(int left, int right) { int returnVal; System.out.printf("Computing sum of elements in range [%d..%d)\n", left, right); if (right - left <= LENGTH_THRESHOLD) { // short segment, so sum its returnVal = sumOfSeq(left,right); // elements sequentially System.out.printf("Sum of elements in SHORT range [%d..%d) is %d\n", left, right, returnVal); } else { // split segment into halves and compute their sums in parallel int mid = (left + right) / 2; // To compute sum of the left half, start a new thread using a // new instance of SumInParallel. SumInParallel leftSIP = new SumInParallel(left, mid); Thread leftThread = new Thread(leftSIP); leftThread.start(); // Compute the sum of the right half via a recursive call. int rightSum = computeSum(mid, high); // Wait for the "left thread" to finish. join(leftThread); returnVal = leftSIP.sum + rightSum; System.out.printf("Sum of elements in range [%d..%d) is %d\n", left, right, returnVal); } return returnVal; } /* Computes the sum of the elements in a[low..high) sequentially and ** returns the result. ** pre: 0 <= low <= high <= a.length */ /* private int sumOfSeq() { int sumSoFar = 0; for (int i = low; i != high; i++) { sumSoFar = sumSoFar + a[i]; } return sumSoFar; } */ /* Computes the sum of the elements in a[left..right) sequentially and ** returns the result. ** pre: 0 <= low <= high <= a.length */ private int sumOfSeq(int left, int right) { int sumSoFar = 0; for (int i = left; i != right; i++) { sumSoFar = sumSoFar + a[i]; } return sumSoFar; } } // end of private class /* Applies join() to the given thread, catching InterruptedException ** if it is thrown. */ private void join(Thread t) { try { t.join(); } catch (InterruptedException e) { e.printStackTrace(System.out); } } // main() method (for testing purposes) // ------------------------------------ public static void main(String[] args) { Scanner keyboard = new Scanner(System.in); int aryLen = getIntInput(keyboard, "Enter array length: "); int lowBound = getIntInput(keyboard, "Enter lower bound value: "); int upperBound = getIntInput(keyboard, "Enter upper bound value: "); int seed = getIntInput(keyboard, "Enter random seed: "); RandIntArrayMaker riam = new RandIntArrayMaker(); // Make an array filled with random integers in accord with // parameters entered by user. int[] ary = riam.randomIntArray(aryLen, lowBound, upperBound, seed); // new int[] { 3, -7, 15, 2, 5, 18, -9, -1, 4, 12, -17, 2, // 19, 31, -7, 8, 0, 4, 16, -11, -2, 9, 6 }; System.out.printf("Array: %s\n", Arrays.toString(ary)); ArraySummerParallel asp = new ArraySummerParallel(); System.out.println("Adding..."); int sum = asp.sumOf(ary); System.out.printf("Sum is %d\n", sum); int sumUsingSeq = sumOfSequential(ary, 0, ary.length); if (sum != sumUsingSeq) { System.out.printf("Oops; seq. alg. yields %d\n", sumUsingSeq); } } /* Returns the sum of the elements in the given array segment, ** ary[low..high), using the obvious sequential algorithm. ** Used for testing purposes. (Can the sumOfSeq() method inside ** the nested class be used instead? Should that method be moved ** so that it is not inside the nested class?) */ private static int sumOfSequential(int[] ary, int low, int high) { int sumSoFar = 0; for (int i = low; i != high; i++) { sumSoFar = sumSoFar + ary[i]; } return sumSoFar; } private static int getIntInput(Scanner scanner, String prompt) { System.out.print(prompt); return scanner.nextInt(); } }