Parallel programming in Java (1/4): In the kitchen (Threading)
Table of contents
Intro
This article is depending on the course (https://www.linkedin.com/learning/parallel-and-concurrent-programming-with-java-1) on linkedin learning
This is part 1 out of 4, to scratch the surface of parallel programming in Java, and how we can make use of threads using Java 11+
In each article, we will have a real-life example to align with the concepts, then we will elaborate on the code.
In the kitchen
Today we are in the kitchen. Chef (A) and Chef (B) are preparing Salad for us. let's see what will they face
Threads VS Process
The process is the combination of actions, info and data needed for a specific action, also it includes the threads, where every thread is responsible for a specific task
So, our kitchen will become the process, where we need to make a salad, Chef(A) will make a thread for chopping cucumber, Chef(B) will make a thread for slicing the onion, we might need another thread so we can include Chef(C) to make the dressing. All of those three chefs are included in the same kitchen and have access to the same resources, the same cookbook and the same vegetables.
We can have another kitchen that runs on making Pizza and another kitchen runs on making a cake.
Concurrency VS Parallel
"WE HAVE ONLY ONE KNIFE", said Chef(A)
Chef(B) smiled and said, "No problem we can work concurrently"
Chef(B) started slicing the onion, then gave the knife to Chef(A)
Chef(A) started chopping some cucumber, then gave it back to Chef(B)
Although it's not the practice, that's how concurrency deals with multiple things at once. We can do it better actually if we get another knife to work with
Now we have two knives, so we can work in Parallel
Thread lifecycle
Chef(B) is called by Chef(A), and now Chef(B) is ready for any orders. standing still and waiting [NEW]
Chef(B) is told Chef(A), I want you to prepare the dressing for the salad. Chef(B) said "Ok, understood. I'll be waiting until you tell me to start"
Chef(B) is told by Chef(A) to start [RUNNABLE]
Chef(B) found out that the mayo for the dressing is frozen, so we need to wait 30 minutes for the microwave. [TIMED_WAITING]
Mayo is ready, and Chef(B) is completing the dressing now [RUNNABLE]
Chef(A) is waiting for the dressing
Chef(B) is now done, and leaving the kitchen [TERMINATED]
Scheduling execution
We have a lot of guests invited for tonight, so we hired more chefs. However we are still having only two knives, so we brought an organizer [Scheduler] to look over those two knives and choose who shall use them.
If one chef ends, the organizer takes the knife and passes it to another ready chef to complete his work
If one chef is pending on another task, then that chef shall give the turn to another chef
If one chef is taking too much time on the job, the organizer swaps that chef with another one to allow the others to complete their job as well [Context switch]
Note that the swap thing is taking time and effort because the organizer shall remember every single detail about the swapped chef, where is the stop point, and how the process shall continue.
Mainly this scheduling thing guarantees that everything is done with maximum fairness, throughput and minimum wait time and latency. However, each company has its regulations and roles regarding how to implement that
Daemon threads
The party is finally over, the chefs are ready to leave the kitchen to close it. However, one of them is still collecting the garbage, so they waited and waited and waited keeping the kitchen full. The best solution for this is to leave the kitchen and close it, and the last chef can keep on collecting things detached from them
In the code
If you start learning Java SE, you will be hit with (Threads) and (Runnable) interface, and (sleep) method. And you have the same questions and answers in each interview. But in these four articles, we are diving deeper than that to see how we can make the best use of those mysterious threads
Threads VS Process
"Process" is the parent or the container, it is an instance of the program, it includes the code, the data and state information. Each process is independent and has its own address space in the memory. You can have hundreds of active processes, and the operating system is responsible for handling them. Not to mention also it contains "Threads"
Threads are the basic unit for the operating system to manage, they are simply an independent path of executions. a bunch of code lines that run and have the same access to the shared code and shared data
Threads are "lightweight" compared to the process, and the operating system can switch between different threads faster than processes
Concurrency VS Parallel
"Can the computer run two operations in parallel?" The answer is simple. If it has more than one core, then yes it can.
But what if it has only one? it may seem like it's parallel, but it's not.
The processor is detaching the detachable tasks and keeps switching between them so that you might think it's working in parallel. But they actually working concurrently.
Creating thread in Java
There are three ways to create a thread in Java, you can use whatever suits you.
class ThreadOne extends Thread {
public void run() {
for (int i = 0 ; i < 10_000; i++)
System.out.println("Thread 1");
}
}
class ThreadTwo implements Runnable {
public void run() {
for (int i = 0 ; i < 10_000; i++)
System.out.println("Thread 2");
}
}
public class Main {
public static void main(String[] args) {
Thread threadOne = new ThreadOne();
Thread threadTwo = new Thread(new ThreadTwo());
// Third way in case of anonymous threads (Or single use threads)
Thread threadThree = new Thread(() -> {
for (int i = 0 ; i < 10_000; i++)
System.out.println("Thread 3");
});
threadOne.start();
threadTwo.start();
threadThree.start();
}
}
But remember, as per best practice, it's better to implement an interface instead of extending an implemented class
Thread lifecycle
In Java, we have 6 states for the thread. At any point in time, you can check the status of the thread using this method (instance.getState())
NEW -> The thread has been instantiated and is ready to run
RUNNABLE -> The thread is executed in JVM (Ready for OS scheduler)
BLOCKED -> The thread is blocked, waiting for a lock (Will talk about locks later)
WAITING -> The thread indefinitely for another thread to perform an action
TIMED_WAITING -> The thread is waiting for a definite time
TERMINATED -> The thread has exited
public class Main {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
System.out.println(thread.getState()); // NEW
thread.start();
System.out.println(thread.getState()); // RUNNABLE
Thread.sleep(500);
System.out.println(thread.getState()); // TIMED_WAITING Waiting for the main thread for 500 ms
thread.join();
System.out.println(thread.getState()); // TERMINATED
}
}
Thread attributes
Used attributes
Thread.sleep(1000); // Sleeps the thread for 1000 milliseconds, shall be surrounded by try-catch block
int count = Thread.getActiveCount(); // Returns the number of active threads running now
Thread instance = Thread.currentThread(); // Returns the current thread that's running
long id = instance.threadId() // To get the ID of the thread (getId) is deprecated later
instance.join() // Make the original thread wait until the instance thread terminates to continute the flow
Thread.State state = instance.getState() // Returns enum of the sattes mentioned earlier
instance.setDaemon(true) // Make the thread daemon (Explained later)
instance.setPriority(1) // Takes integer between 1-10, where 1 is the highest and 10 is the lowest
Run VS Start
The major difference is that, start is creating a new thread and running the (run) method inside it, while run is running the (run) method inside the current thread (Doesn't start a new thread)
Thread thread = new Thread(new Runnable(() -> System.out.println("HI")));
Thread.getActiveCount();
thread.run()
Thread.getActiveCount();
// The getActiveCount will return the same number
thread.start()
Thread.getActiveCount();
// The getActiveCount number will increase as a new thread has joined
Another major difference, you can call the (run) method multiple times. However you can call (start) only once, or it will through an exception
Scheduling execution
You can never count on the sequential of the threads, threads in runnable states are chosen by the scheduler of the operating system to run. So, they don't have to follow specific order or a specific time of implementation
If you run this code you will get different results each time for each thread
class ChopperThread extends Thread {
int total_count = 0;
static boolean chopping = true;
ChopperThread(String name) {this.setName(name);}
@Override
public void run() {
while (chopping) {
System.out.println(this.getName() + ": " + total_count);
total_count++;
}
}
}
public class Main {
public static void main(String[] args) throws InterruptedException {
ChopperThread chopperOne = new ChopperThread("One");
ChopperThread chopperTwo = new ChopperThread("Two");
chopperOne.start();
chopperTwo.start();
Thread.sleep(1000);
ChopperThread.chopping = false;
chopperOne.join();
chopperTwo.join();
System.out.println("Chopper one total is: " + chopperOne.total_count);
System.out.println("Chopper two total is: " + chopperTwo.total_count);
}
}
Daemon threads
Daemon threads are those threads that need be run in the background, and shall not be terminated when the process is terminated.
Garbage collector is one of the most known daemon threads, as it's a thread running in the background clearing unused variables to clear the memory, it shall be detached from the process.
The daemon thread is terminated when all the other threads are terminated and not needed anymore, it's terminated by the JVM
Thread thread = new Thread(new Runnable(() -> System.out.println("HI")));
thread.setDaemon(true); // Now this thread will keep running
Conclusion
Threads are a very important part of Java programming, also it's very easy to deal with just you have to know a few basics and we will cover them thoroughly.
Java threads are more fun and interesting than dull normal courses, and we will dive more into that through the upcoming articles