Java Threads – an Introduction for all Skill Levels

Today we are talking about threads in Java. Threads are an essential means to implement concurrent algorithms and are therefore a key part of efficient everyday data processing. Imagine having a webserver without threads serving only one customer at a time: Every customer would need to pull a number for every single web request they make and our online shop would have a bad time.

So let’s head into it!

In modern days – Java threads with Lambda expressions

Since Java 8 we have been blessed with Lambda expression. Basically Lambda expressions serve the purpose of unnamed functions and are written as shown here:

(param1, param2, [...], paramN) -> {
    statement1; 
    statement2;
    [...]
    statementN;
}

If you just need one statement, it looks even better:

(param1, [...], paramN) -> statement

By harnessing their power we are able to write beautiful first level function style concurrent algorithms.

Let’s assume we want to sort a list with an even number of integers. We know that all ints in the second half of the list are greater than the greates number in the first half. Then we can do this:

import java.util.*;

public class ThreadExampleMain {

    public static void main(String[] args) throws InterruptedException {
        /* Elements of the 2nd half are larger than in the 1st half, otherwise unsorted. */
        List<Integer> test = Arrays.asList(3, 1, 5, 6, 21, 12, 18, 16);
        List<Integer> sortedFirstHalf = test.subList(0, 4);
        List<Integer> sortedSecondHalf = test.subList(4, 8);

        Thread t1 = new Thread(() -> {
            Collections.sort(sortedFirstHalf); // or any other more elaborate sorting algorithm
            System.out.println("First half sorted: " + sortedFirstHalf);
        });

        Thread t2 = new Thread(() -> {
            Collections.sort(sortedSecondHalf);
            System.out.println("Second half sorted: " + sortedSecondHalf);
        });

        t1.start();
        t2.start();
        t1.join();
        t2.join();

        // Merge the sorted list parts together from left to right...
        List<Integer> result = new ArrayList<>();
        result.addAll(sortedFirstHalf);
        result.addAll(sortedSecondHalf);

        // ...and show the result.
        System.out.println("Result: " + result);
    }

}

Try it out!

Notice that we feed the threads in our Java code with Lambdas to tell them what to do. Neat, isn’t it?

Legacy Java Threads with Runnable instances

And how has it been done in the past? When I started programming with Java around 5 and 6 we had to declare a full-blown Runnable-instance that we subsequently fed to the thread. Runnable is an interface type which means that during declaration we also had to implement it’s signature method run(). This method finally contains the functionality that the thread is supposed to run. Let me show what I mean:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class ThreadExampleLegacyMain {

    public static void main(String[] args) throws InterruptedException {
        /* Elements of the 2nd half are larger than in the 1st half, otherwise unsorted. */
        List<Integer> test = Arrays.asList(3, 1, 5, 6, 21, 12, 18, 16);
        List<Integer> sortedFirstHalf = test.subList(0, 4);
        List<Integer> sortedSecondHalf = test.subList(4, 8);

        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                Collections.sort(sortedFirstHalf); // or any other more elaborate sorting algorithm
                System.out.println("First half sorted: " + sortedFirstHalf);
            }
        });

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                Collections.sort(sortedSecondHalf);
                System.out.println("Second half sorted: " + sortedSecondHalf);
            }
        });

        t1.start();
        t2.start();
        t1.join();
        t2.join();

        // Merge the sorted list parts together from left to right...
        List<Integer> result = new ArrayList<>();
        result.addAll(sortedFirstHalf);
        result.addAll(sortedSecondHalf);

        // ...and show the result.
        System.out.println("Result: " + result);
    }

}

Try it out!

It was a bloated mess, but since it’s likely in use in modern day code, we got to list it here.

Conclusion

So long. I hope I could help you effectively processing data by leveraging the power of Java threads. Please note that the examples shown above are optimized for illustration of the topic only. Your way to sort a list of integers is certainly much better. If you’d like to read more about me doing Java, I have a post for you, where I talk about web test automation with Java. If you are already familiar with that and want to try something new, I recommend trying fuzz testing in Java. You will gain a fresh view on test automation and honestly it’s a blast to bomb an application with just random stuff.

Last but not least, if you want to dive in really deep into the world of Java threads, check out this article about Thread Pooling. It is quite advanced, but intuitive and powerful once you get the hang of it.

You have more questions regarding Java, me or anything else? As usual, feel free to drop me a line down below. I read every single one of your comments. Have a great day!

Home » tutorial
Share it with: