Search code examples
javaconcurrency

Running part of the method at the same time in Java


I have a method and I want to run only the parts of the method at the same time. For example, I want to run doSomethingOne, doSomethingTwo and doSomethingThree methods at the same time, but they should start after the start variable and end before the end variable.

public static void someMethod() {
    final ExecutorService pool = Executors.newFixedThreadPool(15);

    long start = System.nanoTime();
    
    pool.execute(() -> {
        doSomethingOne;
    });
    pool.execute(() -> {
        doSomethingTwo;
    });
    pool.execute(() -> {
        doSomethingThree;
    });
    
    pool.shutdown();
    long end = System.nanoTime();
    System.out.println((end - start) / 1_000_000_000L);
}

Solution

  • Wait for tasks to complete

    Your current code will already let all three threads start after start is initialized. However, the shutdown method doesn't wait for all the work to be done.

    CountDownLatch

    You can use a CountDownLatch to wait until all jobs are done:

    public static void someMethod() {
        final ExecutorService pool = Executors.newFixedThreadPool(15);
        final CountDownLatch endLatch = new CountDownLatch(3);
    
        long start = System.nanoTime();
    
        pool.execute(() -> {
            try {
                doSomethingOne;
            } finally {
                endLatch.countDown();
            }
        });
        pool.execute(() -> {
            try {
                doSomethingTwo;
            } finally {
                endLatch.countDown();
            }
        });
        pool.execute(() -> {
            try {
                doSomethingThree;
            } finally {
                endLatch.countDown();
            }
        });
        
        pool.shutdown();
    
        // exception handling omitted
        endLatch.await();
    
        long end = System.nanoTime();
        System.out.println((end - start) / 1_000_000_000L);
    }
    

    There are two things that can mess with timing: the time it takes to schedule the tasks, and the time it takes to shutdown the pool. Another CountDownLatch can help with the first, and reordering the shutdown call with the other:

    public static void someMethod() {
        final ExecutorService pool = Executors.newFixedThreadPool(15);
    
        try {
            final CountDownLatch startLatch = new CountDownLatch(1);
            final CountDownLatch endLatch = new CountDownLatch(3);
    
            pool.execute(() -> {
                try {
                    startLatch.await(); // exception handling omitted
                    doSomethingOne;
                } finally {
                    endLatch.countDown();
                }
            });
            pool.execute(() -> {
                try {
                    startLatch.await(); // exception handling omitted
                    doSomethingTwo;
                } finally {
                    endLatch.countDown();
                }
            });
            pool.execute(() -> {
                try {
                    startLatch.await(); // exception handling omitted
                    doSomethingThree;
                } finally {
                    endLatch.countDown();
                }
            });
    
            startLatch.countDown();
            long start = System.nanoTime();
    
            // exception handling omitted
            endLatch.await();
    
            long end = System.nanoTime();
            System.out.println((end - start) / 1_000_000_000L);
        } finally {
            pool.shutdown();
        }
    }
    

    The start latch acts like a GO signal - everything can prepare itself as necessary, but only actually start when the GO is given.

    Of course, with 3 almost identical blocks of code, you'd create a utility method for that, which only takes the doSomethingOne part.