In this article, we will talk about Threads.
When a Java application starts, a process is created.
Process is an instance of a program at runtime, an independent entity that is allocated system resources (such as CPU time and memory).
Each process runs in a separate address space: one process can’t access the variables and data structures of another. If a process wants to access other processes resources, it must use inter-process communication.
For each process, the OS creates a so-called “virtual address space” to which the process has direct access. This space belongs to the process, contains only its data and is at its complete disposal.
The operating system, on the other hand, is responsible for how the process’s virtual space is projected onto physical memory.
Thread — a definite way of executing a process, which determines the sequence of execution of code in the process.
Threads are always created in the context of a process, and their entire life passes only within its boundaries.
When a Java Virtual Machine starts up, there is usually a single non-daemon thread (which typically calls the method named main
of some designated class).
The JVM allows an application to have multiple threads of execution running concurrently.
Thread in Java represented as an instance of a class java.lang.Thread. java.lang.Thread
in Java is not itself a thread. This is just a kind of API for low-level threads that are managed by the JVM and the operating system.
Architecture of Thread:
A thread can be in one of the following states:
- NEW — A thread that has not yet started is in this state..
- RUNNABLE — This state is for a thread running in the JVM.
- BLOCKED — A thread that is blocked waiting for a monitor lock is in this state.
- WAITING — A thread that is waiting indefinitely for another thread to perform a particular action is in this state.
- TIMED WAITING — A thread lies in a timed waiting state when it calls a method with a time-out parameter. A thread lies in this state until the timeout is completed or until a notification is received.
- TERMINATED — The terminated thread is in this state.
When we create a thread object by use of the new operator, the thread enters in NEW
state. In this state, the thread is not considered alive.
ThreadA threadA = new ThreadA();
The thread remains in the NEW
state until the JVM calls the start()
method. As soon as the start()
method is called, the thread leaves the NEW
state and enters the RUNNABLE
state.
ThreadA threadA = new ThreadA();
threadA.start();
A thread can’t move back to the NEW
state after the call of the start()
method.
After calling start()
method. The control transfers to the Thread scheduler to finish its execution. Whether to run this thread instantly or keep it in runnable thread pool before running, depends on the OS implementation of thread scheduler.
Thread scheduler in Java is the component of JVM that determines the execution order of multiple threads on a single processor (CPU).
The thread scheduler allocates a fixed CPU amount of time to each individual thread and then pauses so that other threads can get a chance to run.
In the RUNNABLE
state a thread can be either RUNNING
or READY_TO_RUN
.
The RUNNING
state doesn’t exist in reality, but it’s considered a part of the RUNNABLE
state. RUNNING
is the state in which the CPU executes the code of the thread. And READY_TO_RUN
is the state in witch the Thread is waiting time of processor for run code.
We understand what happens to a thread when it is RUNNING
, but it can also be BLOCKED
or wait.
In the waiting mode, the thread can be indefinitely, in which case the state will be WAITING
or it can be for some time, in which case the state will be TIMED WAITING
.
A RUNNABLE
thread can move to the TIMED WAITING
state for a specific time interval.
When the thread is in state TIMED WAITING
, this means that the thread is in this state for a certain period of time. It will remain in this state until the specified time interval has elapsed or a notification has been received.
We have some methods that are used to move the thread in the TIMED WAITING
state like sleep(time)
, wait(timeout)
, join(timeout)
, parkNanos()
, parkUntil()
.
For example we haveThreadA
witch make some job.
public static void main(String[] args) throws InterruptedException {
ThreadA threadA = new ThreadA();
threadA.start();
threadA.join(250L);
System.out.println("ThreadA finished work= " + !threadA.isAlive());
}
class ThreadA extends Thread {
@Override
public void run() {
try {
sleep(5000L);
} catch (InterruptedException e) {
return; // Thread termination after interrupt
}
}
}
The join()
method allows the current thread to “suspend” the execution of its code and “skip” forward another thread.
In main
method we wait 250 milliseconds and continue work.
Let’s imagine a situation in which we have a thread BestCurrencyThread
that calculates the best currency rate. And the thread that is updates the currencies is RefreshCurrencyThread
.
Every time we want to get the best rate, our BestCurrencyThread
should try to update the currency rates, but since this can be a long operation and we can wait no more than 250 milliseconds, we call the join()
method with timeout.
If we didn’t have time to get the result within 250 milliseconds, we continue updating but use the previous result.
class BestCurrencyThread extends Thread {
private final RefreshCurrencyThread refreshThread;
public BestCurrencyThread(RefreshCurrencyThread refreshThread) {
super();
this.refreshThread = refreshThread;
}
@Override
public void run() {
refreshThread.start();
try {
refreshThread.join(250L);
} catch (InterruptedException e) {
// ignored
}
calculateBestRate();
}
private void calculateBestRate() {
// todo calculate
}
}
class RefreshCurrencyThread extends Thread {
@Override
public synchronized void start() {
super.start();
try {
Thread.sleep(5000L);
// todo refresh currencies
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
WAITING
state:
When a thread is in the WAITING
state, it means some other threads are on priority. A thread can be in the state for various reasons. We can put a thread in the WAITING
state by calling its wait()
method, join()
method or park()
method.
BLOCKED
state:
When a thread is in the BLOCKED
state it is not eligible to run. But thread will to be alive.
Thread can move to the BLOCKED
state if thread is try to run a synchronized
block but there is another thread currently running inside a synchronized
block on the same object.
For example:
public static void main(String[] args) throws InterruptedException {
Work work = new Work();
Thread threadA = new ThreadA(work);
Thread threadB = new ThreadB(work);
threadA.start();
threadB.start();
Thread.sleep(1000L);
System.out.println(threadA.getName() + " state= " + threadA.getState().name());
System.out.println(threadB.getName() + " state= " + threadB.getState().name());
}
class Work {
synchronized void doWork() throws InterruptedException {
Thread.sleep(5000L);
}
}
class ThreadA extends Thread {
private final Work work;
public ThreadA(Work work) {
super("ThreadA");
this.work = work;
}
@Override
public void run() {
System.out.println(this.getName() + " start work");
try {
work.doWork();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(this.getName() + " finish work");
}
}
class ThreadB extends Thread {
private final Work work;
public ThreadB(Work work) {
super("ThreadB");
this.work = work;
}
@Override
public void run() {
System.out.println(this.getName() + " start work");
try {
work.doWork();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(this.getName() + " finish work");
}
When starting our ThreadA
code, thread call the doWork()
method and waits 5 seconds for this method to complete. At the same time, ThreadB
calls the doWork()
method of the same object. Since the Work object is already occupied by ThreadA
, our ThreadB
enters the BLOCKED
state.
So we understood that when we create a new thread, its initial state is NEW
.
After starting the thread by calling the start()
method, the state changes to RUNNABLE
.
When a thread terminates, interrupt, or crash, its state changes to TERMINATED
.
While a thread is executing, it can have one of the following states: WAITING
, BLOCKED
или TIMED_WAITING
.
If a thread called a synchronized block of code, then its state goes to BLOCKED
.
If the thread calls one of the methods: wait()
, sleep()
, join()
, then it goes into the WAITING
state. To return to the RUNNABLE
state, a thread must wait until some thread calls the notify()
or notifyAll()
method.
For the TIMED_WAITING
state, the situation is the same as for WAITING
, but the methods that bring the thread into the TIMED_WAITING
state are different. These methods are: sleep(time)
, wait(timeout)
, join(timeout)
, parkNanos()
, parkUntil()
.
Once again with examples:
Our ThreadA
calls the doWork()
method.
class ThreadA extends Thread {
private final Work work;
public ThreadA(Work work) {
super("ThreadA");
this.work = work;
}
@Override
public void run() {
System.out.println(this.getName() + " start work");
try {
work.doWork();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(this.getName() + " finish work");
}
}
If any of the threads called the doWork()
method before our ThreadA
thread, then ThreadA
is state will be BLOCKED
until the doWork()
method has completed its work and our ThreadA
thread can start executing the doWork()
method.
Now let’s see how ThreadA can change state to the WAITING state.
Let’s imagine that our thread ThreadA
needs to wait for the execution of ThreadB
at some point in time and only then continue working.
threadB.join();
The join()
method allows the current thread to “pause” the execution of its code and “skip” forward another thread.
When we called the join()
method on a ThreadB
, ThreadA
changed state to the WAITING
state.
start() and run() method:
In the examples above, we start the thread using the start()
method, but the logic of the work is in the run()
method.
Method run()
is the body of the thread that executes when JVM calls the start()
method.
We have two ways to create a thread:
First, create new class and extends from class java.lang.Thread.
class ThreadB extends Thread {
@Override
public void run() {
// todo do something
}
}
Don’t forget about overriding run()
method for to do logic in our thread.
Second,it create a new thread and provided the Runnable
interface in the thread’s constructor.
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
}
});
thread.start();
Runnable
is an interface that represents an abstraction over a running task.
In addition to helping to solve the problem of multiple inheritance, Runnable
is a clear advantage of using it is that it allows you to logically separate the execution logic of a task from direct flow control.
How does the start() method work?
Let’s look to start()
method:
public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
We see that the start()
method is synchronized
, only one thread can start any thread at the current time.
Method start()
move thread from NEW
state to RUNNABLE
.
Calling the start()
method twice on the same Thread
object will result in an IllegalThreadStateException
being thrown at runtime
.
Also in method start()
add the current thread to the ThreadGroup
.
Threads can form a thread hierarchy by being added to a ThreadGroup
. Threads belonging to a group can be controlled at the same time — we can interrupt
the work of all the threads of the group at once, or set a single maximum execution priority
value for them.
What if we want to stop the execution of the thread?
There is no method in Java that would force a thread to stop.
Java has a built-in thread notification mechanism called Interruption
.
Interruption signals that the thread should terminate its work. We cannot terminate the thread immediately. Interruption asks the thread to finish gracefully.
You can use the Thread — interrupt()
class method to correctly notify you that a thread has stopped.
This method sets some internal interrupt
status flag. Later, the state of this flag can be checked using the isInterrupted()
or Thread.interrupted()
method.
Let’s look for example:
public static void main(String[] args) throws InterruptedException {
Work work = new Work();
Thread threadA = new Thread(work);
threadA.start();
if (!threadA.isInterrupted()) {
threadA.interrupt();
}
System.out.println(threadA.getName() + " isInterrupted= " + threadA.isInterrupted());
}
// ThreadA isInterrupted= true
We start ThreadA
, then we check whether there was an interruption of the current thread, and if not, then we call the interrupt()
method.
The interrupt() method is also capable of waking a thread from a waiting or sleep state. Those, if a thread has called sleep() or wait() — the current state will be interrupted and an InterruptedException eception will be thrown.
It is important to understand that interrupt is addressed not only to the code that is running on the thread, but also to the owner of the thread.
@Override
public void run() {
try {
work.doWork();
} catch (InterruptedException e) {
}
System.out.println(this.getName() + " isInterrupted= " + isInterrupted());
}
// ThreadA isInterrupted= false
When we get an InterruptedException
error, the isInterrupted
flag is cleared.
The correct code will look like this:
@Override
public void run() {
try {
work.doWork();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println(this.getName() + " isInterrupted= " + isInterrupted());
}
// ThreadA isInterrupted= true
When receiving an InterruptedException
error, we must set the isInterrupted
flag to true
so that we can further check that the thread needs to be suspended and be able to properly finish executing its work.
A small summary of Interruption:
It should be understood that when one thread calls the interrupt()
method on another thread, this does not stop the thread, but only notifies that it should complete its work.
public static void main(String[] args) throws InterruptedException {
...
threadA.interrupt();
}
In the example above, on thread main
, it is calling the interrupt()
method on the ThreadA
thread. For the main
thread, it turns out that ThreadA
has completed its work, but ThreadA
only received a notification that it would not be bad to complete the work and must decide how to complete it.
In turn, if we called the interrupt()
method for the ThreadA
thread at the time when it called the sleep()
, join()
, wait()
methods, then the thread receives an InterruptedException
error. ThreadA
must handle this error and set the is Interrupted
flag to true
by calling Thread.currentThread().interrupt();
Further inside the thread, we can call the isInterrupted()
method to check if our thread should be terminated.
If a thread performs I/O
operations and call the interrupt()
method on it, then this will not stop its execution.
The solutions here differ depending on the type of data source.
If reading is coming from a file, a long-term lock is extremely unlikely, and then you can just wait for the read()
method to exit.
If the read is somehow connected to the network, you should use non-blocking I/O
from Java NIO
.
NIO
has such a nice feature that it actually takes thread interrupts into account. If we try to read or write to the channel after the thread is interrupted, thread will get a ClosedByInterruptException
.
Method stop():
Thread has a stop()
method, why not call it?
When a thread is forced to stop, the stop()
method interrupts the thread at a non-deterministic execution location, as a result, it becomes completely unclear what to do with the resources that belong to it.
The thread can open a network connection — what to do with the data that has not yet been subtracted?
Where is the guarantee that after a further launch of the thread, he will be able to finish reading them?
If a thread has locked a shared resource, then how to release this lock and won’t forced release lead to system consistency violation? The same can be extended to the case of a database connection: if the thread is stopped in the middle of a transaction, then who will close this transaction?
Uncaught Exceptions:
If an exception occurs inside a thread that is not handled, then such a thread “dies”.
Let’s consider a situation when our thread received Exception
but we throw
an exception above.
We start the thread ThreadA
and while the thread is in the waiting state, we interrupt it.
public class ProgramRunTest {
public static void main(String[] args) throws InterruptedException {
Work work = new Work();
Thread threadA = new ThreadA(work);
threadA.start();
if (!threadA.isInterrupted()) {
threadA.interrupt();
}
}
}
class Work {
synchronized void doWork() throws InterruptedException {
Thread.sleep(5000L);
}
}
class ThreadA extends Thread {
private final Work work;
public ThreadA(Work work) {
super("ThreadA");
this.work = work;
}
@Override
public void run() {
try {
work.doWork(); // throw InterruptedException
} catch (InterruptedException e) {
throw new RuntimeException("error message");
}
}
}
In the console we can see that our program ended with the message:Exception in thread “ThreadA” java.lang.RuntimeException: error message
.
at run.ThreadA.run(ProgramRunTest.java:37)
We can handle exceptions to take some action for example close connection with Database and finished work of thread.
First what we can to handle exceptions, we can use a block: try catch finally
.
Second, it's UncaughtExceptionHandler
.
Uncaught Exception Handler:
If a uncaught exception handler is installed, it will take control.
Let's create ExceptionHandler
:
class ExceptionHandler implements Thread.UncaughtExceptionHandler {
@Override
public void uncaughtException(Thread thread, Throwable t) {
System.err.println((thread.getName() + "error= " + t.getMessage()));
// ... Handle the exception
}
}
And add our example with handle error.
public static void main(String[] args) throws InterruptedException {
Thread.UncaughtExceptionHandler handler = new ExceptionHandler();
Work work = new Work();
Thread threadA = new ThreadA(work);
threadA.setUncaughtExceptionHandler(handler);
threadA.start();
if (!threadA.isInterrupted()) {
threadA.interrupt();
}
}
Our program don’t crash and continue its work as the exception will be handled in the ExceptionHandler
.
More options for exception handling are provided by ThreadPoolExecutor
and Feature
.
These classes will be covered in the following parts.
What is thread priority?
Every thread has a priority. Threads with higher priority are executed in preference to threads with lower priority.
In theory, high priority threads get more CPU time than low priority threads. In practice, the amount of CPU time a thread receives often depends on several factors besides its priority.
To set the priority of a thread, use the class method Thread: final void setPriority(int level)
.
The value of level ranges from Thread.MIN_PRIORITY = 1
toThread.MAX_PRIORITY = 10
.
Default Priority:Thread.NORM_PRlORITY = 5
.
You can get the current thread priority value by calling the method:final int getPriority()
in thread.
What are daemon threads?
Daemon
threads run in the background with the program, but are not an integral part of the program.
Daemon threads are low-priority threads whose only role is to provide services to user threads.
Daemon
thread can be started by calling the setDaemon(boolean value)
method on the thread before it starts.
daemonThread.setDaemon(true);
The boolean isDaemon()
method allows you to determine whether the specified thread is a daemon or not.
daemonThread.isDaemon()
When all user threads have finished, the JVM exits. Daemons do not perform independent tasks, so they do not prevent stopping, the program ends without waiting for their work to finish.
A daemon thread can be useful for things like cache invalidation, periodically refreshing values from external sources, and freeing up unused user resources.
ThreadLocal:
ThreadLocal
— is such a type for a field bound to a thread. That is, each thread has its own field. Even if this field is a static field, it will still be different for each thread.
ThreadLocal
allows having one variable to have a different value for each of the threads.
public class ThreadLocalRunnable implements Runnable {
private ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>();
@Override
public void run() {
threadLocal.set( (int) (Math.random() * 100D) );
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
System.out.println(threadLocal.get());
}
}
public static void main(String[] args) throws InterruptedException {
ThreadLocalRunnable sharedRunnableInstance = new ThreadLocalRunnable();
Thread thread1 = new Thread(sharedRunnableInstance);
Thread thread2 = new Thread(sharedRunnableInstance);
thread1.start();
thread2.start();
thread1.join(); //wait for thread 1 to terminate
thread2.join(); //wait for thread 2 to terminate
}
We can't use ThreadLocal
with ExecutorService
.
If we want to use an ExecutorService
and pass a Runnable
to it, using ThreadLocal
will give non-deterministic results because we have no guarantee that each Runnable
action for a given method will be handled by the same thread every time it is executed.
Because of this, our ThreadLocal
will be shared between different threads. Therefore, we should not use TheadLocal
along with ExecutorService
.
It follows from this that ThreadLocal
can only be used when we have full control over which thread chooses to execute on itself.
Conclusions:
Threads
are very useful whenever a process has multiple tasks to perform independently of the others.
Thread
— a certain way of executing a process, which determines the sequence of execution of code in the process.
The class Thread
contains several methods for managing threads:
start()
— starts a thread- run() — method to execute thread code
- getName() — return name of thread
- getPriority() — return priority of thread
- isAlive() —determine if a thread is running
- join() — allows the current thread to “suspend” the execution of its code and “skip” forward another thread.
- sleep() — pause a thread for a given time
Working with threads looks pretty simple and obvious until we start communication between threads.