Today I want to introduce the topic of Java multithreading to you. This is a bit of an advanced topic, so if you are not familiar with Java programming, I would recommend starting with the basics. If you have been around the Java programming block and have not yet tried your hand at multithreading, then that's great! Let's get started.

What is Java Multithreading?

In Java, a Thread is essentially the Object that represents one piece of work. When you start your application and it starts to run, Java has “spawned” (created) a Thread and this Thread is what will carry out the work that your application is meant to do. What's interesting to note, is that one Thread can only do one particular task at a time. So that would mean it's a bit of a bottleneck if your entire application just works off of one Thread right? Right!

Java multithreading allows you to do multiple tasks at the same time. This is possible because modern day computers have multiple CPUs (CPUs are the brain of your computer, and it has a bunch!). One CPU can work on one Thread at a time (unless your CPUs have hyper-threading, in which case it can handle two at a time). So this means that if your computer has 4 CPUs with hyper-threading technologies, your code could potentially handle 8 Threads at the same time. Neat!

The implications of this are that you can take your code and make it perform MUCH better by introducing the use of multithreading. That is of course, if your program would benefit from the use of multithreading, some applications are fairly simple and things would just get over-complicated by adding in Thread logic.

How do we use it in Java?

Well, it's really not too tough to implement the use of Threads in Java. The trick is ensuring that all the Threads cooperate properly with each other… but I'll get into that after I show you an example of how to set yourself up with Threads.

All you need to do to use Threads is to have an Object implement the Runnable interface and override the run method. Here's an example of a Class named Worker.

public class Worker implements Runnable
{
  public static void main (String[] args)
  {
    System.out.println("This is currently running on the main thread, " +
    		"the id is: " + Thread.currentThread().getId());
    Worker worker = new Worker();
    Thread thread = new Thread(worker);
    thread.start();
  }

  @Override
  public void run()
  {
    System.out.println("This is currently running on a separate thread, " +
    		"the id is: " + Thread.currentThread().getId());

  }
}

When this code is run, here's the output I get on my computer:

This is currently running on the main thread, the id is: 1
This is currently running on a separate thread, the id is: 9

So you can see here that there are two different Threads running here. Now in this example, there's really no need to be using two different Threads because the flow of this code is linear. The trick here would be to introduce the need for multiple Workers to be running at the same time, and to have a lot of work for these Workers to carry out. We can easily create lots of Workers, I'll just use a for loop to create a handful. But how could we simulate lots of work? Well, we could use the Thread.sleep() method; this method pauses the thread for a custom defined period of time. When we pause a Thread, this would simulate that Thread being busy doing some sort of actual work! Sweet, so let's see what that would look like:

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class Worker implements Runnable
{
  public boolean running = false;

  public Worker ()
  {
    Thread thread = new Thread(this);
    thread.start();
  }

  public static void main (String[] args) throws InterruptedException
  {
    List<Worker> workers = new ArrayList<Worker>();

    System.out.println("This is currently running on the main thread, " +
        "the id is: " + Thread.currentThread().getId());

    Date start = new Date();

    // start 5 workers
    for (int i=0; i<5; i++)
    {
      workers.add(new Worker());
    }

    // We must force the main thread to wait for all the workers
    //  to finish their work before we check to see how long it
    //  took to complete
    for (Worker worker : workers)
    {
      while (worker.running)
      {
        Thread.sleep(100);
      }
    }

    Date end = new Date();

    long difference = end.getTime() - start.getTime();

    System.out.println ("This whole process took: " + difference/1000 + " seconds.");
  }

  @Override
  public void run()
  {
    this.running = true;
    System.out.println("This is currently running on a separate thread, " +
        "the id is: " + Thread.currentThread().getId());

    try
    {
      // this will pause this spawned thread for 5 seconds
      //  (5000 is the number of milliseconds to pause)
      // Also, the Thread.sleep() method throws an InterruptedException
      //  so we must "handle" this possible exception, that's why I've
      //  wrapped the sleep() method with a try/catch block
      Thread.sleep(5000);
    }
    catch (InterruptedException e)
    {
      // As user Bernd points out in the comments section below, you should
      //  never swallow an InterruptedException.
      Thread.currentThread().interrupt();
    }
    this.running = false;
  }
}

So what's the output of the code above? Here's what it says on my computer:

This is currently running on the main thread, the id is: 1
This is currently running on a separate thread, the id is: 9
This is currently running on a separate thread, the id is: 10
This is currently running on a separate thread, the id is: 11
This is currently running on a separate thread, the id is: 12
This is currently running on a separate thread, the id is: 13
This whole process took: 5 seconds.

Cool, so that's what I would expect to see. If you look at the code, each worker "sleeps" for 5 seconds, this simulates 5 seconds of work that they are doing. So since we are using multithreading, firing up 5 different workers to carry out 5 seconds of work each should take 5 seconds total. So what happens if we take out the concept of multithreading? How long would this process take? We it would have to create each Worker one at a time and have each one carry out their work in a linear fashion, so it would take 25 seconds to complete. So technically, by implementing multithreading here, we have increased the speed of our processes by 500%! Not too shabby.

Pitfalls of Java Multithreading

One thing I didn't touch on in this article is what's known as Synchronization, and this is the bane of the multithreading programmer's existence. This is because when you introduce the concept of multithreading, you are opening up the possibility of two Threads accessing/modifying the same Object in memory at the same time. When you try to do this, Java will throw an Exception. The solution to this problem is to synchronize access to your Objects so that no two Threads can trample over each other when trying to access any Object's properties/resources.

Another problem that arises with the use of Java multithreading is race conditions. This is when your code's output is based on a particular sequence of events, but with multithreading this is not a great idea, because you can't always guarantee that a particular set of events will happen in any particular order. Here's a wiki article explaining this problem: http://en.wikipedia.org/wiki/Race_condition.

I won't be touching on these concepts just yet, as I have already covered a lot for you to take in. The knowledge in this Java tutorial will be critical in completing the next Java practice assignment, so if you intend on completing it, be sure to understand what I'm explaining in this entire article.

Free Java Beginners Course

Start learning how to code today with our free beginners course. Learn more.