Back to the basic — Thread (with Fish and Chips)

Photo from BBC: https://www.bbc.co.uk/newsround/48553013

Intro & Updates…

And yes. I am back again! These days I have been learning design patterns in Java, SpringBoot, parallel and concurrent programming in Java. So today let me talk about Threads (with Java as an example).

What are Threads?

Threads are lower-level building blocks (sequence of programmed instructions) for (concurrent) programs, where it can be scheduled by the scheduler within the Operating System and executed by the processor.

When we talk about threads, we have to understand them as a unit of execution within the processor. Every Operating System supports Thread. It is the assembly language of parallel execution. With the help of threads, we are able to achieve multitask with different tasks perform on different threads, to be able to complete missions in a much faster way.

Additionally with the improvement of multicores processors nowadays, we can complete tasks parallelly which reduces the cost and the time on scale machine spent on hardware.

How OS works with threads?

Definition of Operating System and Processors

An Operating System is the software for a computer user to interact with the computer hardware for performing what the user wants to achieve.

When the computer is turned, the operating system is loaded from the Hard Drive into the memory. When a user runs the application, the operating system takes the program from the hard drive and creates an instance (this instance is called process/context) of the application in the memory. Each process is completely isolated from any other process that is running on the system. Depending on different Operating Systems, aprocess may be made up of multiple threads of execution that execute instructions concurrently.

A Process is the instance of a computer program that is being executed by one or many threads. It contains the program code and its activity.

What do thread and process contain, and the relationships between them?

Within a thread (no matter the main thread or any threads created by the programmer) there are 2 major things inside. The Stack memory region and the Instruction Pointer.

Stack memory region

The Stack region in the memory is used to store local variables that are declared in the Application Code we mentioned above. Whenever we created a thread, a new Stack region will be allocated for that thread, and variables stored within each Stack region cannot be shared with different threads but Heap.

Instruction Pointer

The instruction pointer is a reference pointer that points to the next instruction that a thread is going to execute. Without the instruction pointer, the processor will not able to know which instruction to perform in the application code written.

How parallelism is done?

You, as the manager (called main), represent the main thread in the static void main method when we run Java. Think in this way will allow you to understand parallelism easily, where 2 workers (2 threads) are recruited (created) by you (in the main thread).

Let us think about a case where a customer came and order Fish and Chips. How would you write your pseudo code to complete the task for the client?

Imagine the time taken to cook for Mushy Peas, Cod Fish and Fries all require different duration with 3, 6, and 9 minutes to complete respectively.

In sequential programming, we learned to write sequences of instructions to the processor to know which instruction to execute. Just like the diagram below, you as the manager of the Fish Bar can ask John to prepare all 3 ingredients and serve the dish. All the deep-fried processes for both the fish and potato and making the mushy peas are all done by John.

The total time to complete a Fish and CHips dish took 18 minutes and it is very inefficient as both you and Fred are actually doing nothing at all while John is trying to do everything while waiting for one to complete before starting another one.

We can see that the total time taken is actually not efficient in a sequential programming way. The critical path length, in this case, needs 18 units of time to complete (in this case the unit is minute). How about asking you and Fred to join, to cook things in parallel while each of you is responsible for each station?

By performing tasks in parallel (with the help of thread creation), we have saved a lot of time to finish a mission.

Let say you are lazy and would like to only prepare for mushy peas as it took the least amount of time, while John prepare the Cod and Fred prepare the chips. The critical path length is now shortened to only 9 units of time! By running the program in parallel, it enhances the performance of the process. Where in reality, you, John, and Fred are threads scheduled by the processor that we mentioned above in an operating system. We will talk about scheduling if I have a chance to come across it later on but let us try to jump into the code of the example!

Runnable Interface

Runnable is an interface that we will need to ask the Thread class to perform a certain task that we want.

There are many ways to override the run method within Runnable but in our case, we will split it as a WorkerThread class (I changed the name to Worker in the later on example so please be aware… ) to store some information we need on a chef (task given information such as chef name, ingredient name, and time required to complete).

In the run method, we have will ask the current thread to sleep for the time we set when we create the Worker class. Each print contents represent each status when the time flows. What we want to achieve is the following situation during thread creation and execution.

You (the main thread) will start together with John and Fred to cook ingredients at the same time. as Mushy peas took only 3 minutes so once you finished your task, you will have to wait until the other 2 threads (John and Fred) to finish their work and handle what they finish by you. This can be achieved by the join method.

The current thread that runs another join method on another thread will let the current thread notice and wait at the line of code where .join() method is used and wait until that another thread comes back.

With that Thread A using the .join() method:

E.g. (line 68 to line 71 is running in Thread A)

line 68 -> Statement A

line 69 -> Statement B

line 70 -> threadB.join();

line 71 -> Statement C

What that means, thread A will wait until thread B comes back at line 70 where threadB.join() is executed. Only if thread B comes back, then Thread A will execute line 71.

Meanwhile, a managerCook function in main was created to pretend the manager is cooking. Before the function come to the end, you as the manager will check if John and Fred have done their job with quality then serve the dish.

In the main function, we created a Thread object in Java to let it run what is inside the Worker class we created. A thread object will need an object that is created by implementing the Runnable class in order to create the thread object. Both threads will start execution in the managerCook function.

With the output shown below:

Let's set the time taken for mushy peas to 8 minutes and see the result as what we expected!

Conclusion

P.S. Whoever goes to London for Fish and Chips, this one is my favorite so far:

Golden Chippy: 62 Greenwich High Rd, London SE10 8LF
The Cod is amazing ><
But please do not have it everyday lol, very bad to your own health!

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store