The Definitive Guide to Java Threading

Java Multi-Threading Series: Part 1

Learn Java threading in Layman’s terms

Brendon Cheung
May 24 · 5 min read

Part 2 :)

This series is about threads in java, from basic to advance topics like the synchronized keyword, atomic wrappers, understanding atomicity and more.

This post will cover a very high level concepts of threading, you will progressively see more code as the series grow.

In order to set the stage for this complex topic, a hello world example will be used, this application does only one thing which is to print “hello, world” to the console.

Before we begin, keep this quote mind from the book “Java 6, Study Guide”

When it comes to thread, very little is guaranteed

Agenda:

  1. What is a thread and its role
  2. The main thread
  3. Thread life cycle
  4. Thread scheduler
  5. Thread priorities

What is a thread

In java, threads can mean two things:

  1. An instance of a class java.lang.Thread
  2. A thread of execution

From the first point, yes thread is an object, just like any other objects you encounter in programming, it has variable and methods, lives and dies in the heap, just like a Cat object or a Dog object.

In the second point, a thread can be a path of execution, it is an individual process that has its own call stack. For example, when you first start your hello world application, there is a thread executing your main() method, that thread is called the main thread, which is created by the Java Virtual Machine (JVM).

So, in short, threads execute methods

The main thread

A hello world application is deceptively simple, but there are a lot of complexity happening in the background that we should study upon.

You see, the main method is executed by a thread called the main thread and it is created by the JVM at run time.

The sole purpose of a main thread is to execute the main() method

This main thread belongs to a group called “main” thread group, and this group is a child of “system” thread group which contains other threads called daemon threads which does following tasks:

  1. Finalizer (garbage collector)
  2. Reference handler
  3. Signal dispatcher
  4. Attach listeners

Thread life cycle

Normally, there are four life cycle stages to a thread

  1. Born/ new state — This is when you create a thread object using the new keyword
  2. Runnable — This is when you execute the start() method (more on this later)
  3. Running — When the thread scheduler allocate processor to the thread
  4. Dead state — when the thread returns from the run() method (more on this later)

Born/ New → Runnable → Running → Dead

Here are the key points:

  1. When we create a thread object using new Thread() , the object will live in the heap like any other object. This thread is in the born/ new state
  2. When you call start() method on a thread, when the method returns, it will be in a runnable state. Why runnable? because it is up to the thread scheduler to actually run the code inside of the run() block.
  3. Once the thread scheduler allocate processing time to the thread, it will be in a running state
  4. As soon as the thread returns from the run() method, it will be in a dead state

Therefore, the only way to terminate a thread is to arrange for its run() method to complete

Thread scheduler

Here is another quote from “Java 6, Study Guide”

The order in which runnable threads are chosen to run is not guaranteed

The main role of the thread scheduler is to decide which thread should receive processing time from the processor, threads will be queuing up in front of the thread scheduler and the thread scheduler coordinates which thread get the processor time.

The key takeaway is just because the thread scheduler allocated processing time to a thread, it doesn’t guarantee it will complete the execution.

This is known as context switching where the thread scheduler switches from thread to thread, while switching, the thread scheduler will save the current executing state in order to continue once the thread has the opportunity for processing time. Keep in mind the context switching adds performance overhead to your application.

  1. Thread-1 gets allocated processing time from the thread scheduler
  2. Context switch happened because thread scheduler decided to give Thread-2 processing time
  3. Thread-2 is executing,
  4. Context switched happened again, now Thread-1 is executing and returns from the run() method.

Another analogue to think about thread scheduler:

Have you ever come across an intersection where the traffic light are not working but instead an operator is directing the traffic? you can think of the operator as the thread scheduler and the lanes are the threads.

Thread priorities

We set priorities in our daily lives, but sometime just because we set an action item priorities to high, it doesn’t necessary mean they will get done first. Same applies to threads.

Thread priority is an indicator to thread scheduler about the importance or the urgency of a particular thread, the priority level ranges from 1 to 10, 1 being least important and 10 being the most important.

In the threading world, nothing is guaranteed, period. We have the ability to set thread priorities but it does not necessarily mean it will be executed first if there is another thread at a lower priority.

For example, if I ask you this question:

Imagine there two threads, Thread-1 at a default priority of 5 and Thread-2 at a priority of 10. Which thread will receive processing time first?

Intuitively, you will think of course Thread-2 will get processing time because Thread-1 is at a lower priority.

The correct answer should be:

Thread-2 will have a higher probability of executing before Thread-1 because Thread-1 has a lower priority than Thread-2. However, this behavior is not guaranteed.

It is all about probabilities, you cannot predict what the thread scheduler will do because it varies from JVM to JVM. If a thread has a higher priority than another thread, all that means to you is that the thread has a higher probability to executing before the other thread. However, not guaranteed

I hope you enjoyed reading part 1 and understand threading on a top level. In part 2, we will explore the following topics.

In the next series, I will cover the following topics:

  1. Thread groups
  2. Daemon threads
  3. Thread creation
  4. Inheritance of thread properties

I hope you guys find this valuable, if you learned something, drop a clap! Thank you for reading.

Want to be a lambda expression pro master guru zen?! this is for you :)

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade