A Java Thread is a lightweight process that executes a task and shares process code and resources such as memory space, process variables and file handles. A Java Thread is an independent path of execution through a program and can access any object within the process so care should be taken when these resources are accessed. One can think of each thread as running in its own context; each thread appears to have its own CPU, with data, memory and its own code. The Java Virtual Machine support multiple threads running concurrently within the same process. Programs that run more than one task at any time are said to be multithreaded.
Multithreading allows multiple threads to exist and execute simultaneously within the context of a single process. It will allow for multiple tasks to execute on multiprocessor systems. This, in turn makes better utilization of CPU resources and makes programs appear much more responsive.
Multithreading is extremely useful in practice: for example, a mail client like Microsoft Outlook or Mozilla Thunderbird allows us to read our email or compose an email, while it checks for new email, and downloads incoming emails all at the same time. By multithreading we can improve the user experience and performance of the application by fully utilizing CPU resources and using what would otherwise be idle CPU time.
Threads exist within a process and every process has at least one thread — the main thread. Java makes it quite simple to create and manage threads, but multithreading can get very complex. As mentioned earlier, introducing multithreading into applications allows us to exploit the CPU, especially multi-core CPUs. It makes our applications more responsive and allows for the handling of asynchronous events. At the same time, it also introduces complexities and risks like race conditions, deadlocks, blocking, thread starvation which can lead to poor performance.
With the following examples, we will cover many useful topics of Java Threads, Synchronization, Concurrency Programming, and multithreading.
Understand the different states of a java thread and its lifecycle. This tutorial will show you the thread state diagram and how the states change during execution in a java process.
In this post, I will cover creating Java Threads using the two mechanisms provided in Java, that is, by extending the Thread class and by implementing Runnable interface.
All Java threads have a certain priority associated with them. In this tutorial we will discuss the usage of the setPriority() method.
Sometimes we will need to organize and group our threads into logical groupings to aid in thread management. By placing threads in a threadGroup all threads in that group can be assigned properties as a set, instead of going through the tedious task of assigning properties individually.
We seem to use the Thread.sleep() method very often to temporarily suspend the current threads execution for a specific period of time. Let’s spend some time and familiarize ourselves with what this method actually does.
In Java, using Thread.join() causes the current thread to wait until the specified thread dies. Using this method allows us to impose an order such that we can make one thread wait until the other completes doing what it needed to do, such as completing a calculation.
When we declare a field as volatile, the JVM will guarantee visibility, atomicity and ordering of the variable. Without using the volatile keyword, the data may be cached locally in CPU cache as as a result changes to the variable by another thread may not be seen by all other threads resulting in inconsistent behaviour.
The purpose of using notify() and notifyAll() is to enable threads to communicate with one another via some object on which to performing the locking. A thread using the wait() method must own a lock on the object. Once wait() is called, the thread releases the lock, and waits for another thread to either call notify() or notifyAll() method.
Deadlock is a condition where several threads are blocking forever, waiting for the other to finish but they never do. This tutorial will discuss situations that will lead to Java Thread deadlock conditions and how they can be avoided.
In addition, we will discuss using Java VisualVM, which comes bundled with every JDK version and will aid the development team to pinpoint and analyze the source of the deadlock conditions.
Starvation occurs when a thread is continually denied access to resources and as a result it is unable to make progress. Thread liveLock is a condition that closely resembles deadlock in that several processes are blocking each other. But with livelock, a thread is unable to make any progress because every time it tries the operation always fails.
One of Java’s many strengths come from the fact that it supports multithreading by default as has so from the very onset. One of the mechanisms that Java uses for this is via synchronization. When we use the synchronized keyword in Java we are trying limit the number of threads that can simultaneously access and modify a shared resource. The mechanism that is used in Java’s synchronization is called a monitor.
In this tutorial we cover many examples of creating thread-safe singleton classes and discuss some of the shortfalls of each and provide some recommendations on best approaches for a fast, efficient and highly concurrent solution.
In this tutorial we will focus primarily on using the concurrent utilities and how these can make concurrent programming easier for us. The concurrent package provides some additional functionality when we may require more control over synchronization than the standard synchronized keyword.