3.7. Parallelizing Operations
When expressing operations as functional interfaces, the caller gives up control over the processing details. As long as the operations are applied so that the correct result is achieved, the caller has nothing to complain about. In particular, the library can make use of concurrency. For example, in image processing we can split the image into multiple strips and process each strip separately.
Here is a simple way of carrying out an image transformation in parallel. This code operates on Color[][] arrays instead of Image objects because the JavaFX PixelWriter is not threadsafe.
public static Color[][] parallelTransform(Color[][] in, UnaryOperator<Color> f) { int n = Runtime.getRuntime().availableProcessors(); int height = in.length; int width = in[0].length; Color[][] out = new Color[height][width]; try { ExecutorService pool = Executors.newCachedThreadPool(); for (int i = 0; i < n; i++) { int fromY = i * height / n; int toY = (i + 1) * height / n; pool.submit(() -> { for (int x = 0; x < width; x++) for (int y = fromY; y < toY; y++) out[y][x] = f.apply(in[y][x]); }); } pool.shutdown(); pool.awaitTermination(1, TimeUnit.HOURS); } catch (InterruptedException ex) { ex.printStackTrace(); } return out; }
This is, of course, just a proof of concept. Supporting image operations that combine multiple pixels would be a major challenge.
In general, when you are given an object of a functional interface and you need to invoke it many times, ask yourself whether you can take advantage of concurrency.