The Hidden Benefits of Java Multithreading

Multithreading has been a first-class feature of the JVM and the Java programming language right from the start.

When we think of the benefits of multithreading in Java, the following two often come to mind: 

  1. High performance through parallel execution.

  2. Multitasking achieved through concurrency.

hidden_benefits_of_multithreading.jpg

The benefit of high performance translates directly into money, since fast and efficient applications consume fewer resources and therefore require less expensive hardware or fewer cloud instances.

Multitasking achieved through multithreading allows us to execute multiple tasks within an application instance, which results in high responsiveness of user-interfacing applications.

If you want to learn how to create threads and achieve high performance with Java, click here.

However, there is a third benefit to multithreading that is often overlooked. That is task isolation. Each thread execution in the JVM is isolated from the other threads within the same process.

Although threads within the same process share many resources, a thread has very limited ability to interfere with other threads within the process. This is beneficial because aside from a few specific exceptions, a crashed or a misbehaving thread will not bring down the entire application, let alone other threads.

For example, if a thread dies due to an I/O exception or even a NullPointerException, other threads remain unaffected.

To illustrate the power of thread isolation, let's consider the architecture of a video streaming application:

Multithreaded video streaming application architecture diagram

Multithreaded video streaming application architecture diagram

In this application, data is received from the network by the data capturing thread. Audio is captured from the microphone using the audio capturing thread. And video is captured from an external camera by the video capturing thread. The three data streams are synchronized based on their timestamps by the synchronization thread and later are independently streamed to a live audience as well as saved to a file which can be watched at a later time.

Since each task is isolated in a separate thread, if we lose one of the sources like the data or the audio for example, the rest of the media will continue being broadcasted and saved to the file.

Similarly if the streaming thread crashes, the file saving thread will continue saving the unified media into the file.

The exceptions to the threads isolation rule are:

  1. When other threads depend on the misbehaving thread to continue their own work or to finish their calculation.

  2. When the error that brought down the thread affects shared resources or the entire process, like in the case of an OutOfMemoryException.

If you want to learn how to create threads and achieve high performance with Java, click here.

When either of those happen, the application is rarely recoverable and often requires a restart.

In the example above, the isolation breaks if the synchronization thread crashes, since the streaming and file saving threads depend on it.

Those problems can be mitigated by a few multithreading architectural tricks. For example, a watchdog thread can monitor the synchronization thread’s health and in an emergency situation may either:

  1. Launch another synchronization thread to take its place.

  2. Start dumping the input data video and audio to the disk as independent backup files so they can be recovered in the future.

Another vulnerability in this type of architecture is the shared queues that feed the data, video and audio from their capturing threads into the synchronization thread. If those queues grow to a point where our process runs out of memory and we get an OutOfMemoryException, our application can get into a corrupted state.

However, if we implement this multithreaded “multi-producer, multi-consumer” architecture with the right coordination and back pressure techniques, all of which are covered in depth in the Java Multithreading, Concurrency and Performance Optimization course, all these problems can be avoided.

Java multithreading is one of the most powerful tools in any Java developer and software architect's toolkit. It can save us a lot of money on infrastructure, bring superb user experience to our clients, and as we learned in this article allows us to build fault-tolerant applications using thread isolation.

If you want to learn how to build your own highly performant multithreaded applications, take our Java Multithreading, Concurrency and Performance Optimization course.

More Articles

Previous
Previous

Top 3 Tips to Improve your Java Application’s Performance

Next
Next

Apache Kafka for Modern Distributed Systems