Start Concurrent Programming with Java Threads




Image credit - Pixabay

Idea of Concurrent Computing

Concurrency is a property of a certain computational problem that has the ability to be decomposed in to parts. Then, we can solve each of these parts separately. The combination of the results will ultimately lead to the final solution.

My previous article explains a lot. Click here to check it out. Highly recommended to read this first, if you are new to Java concurrent programming or if you want to know the fundamental idea and related things in concurrency. Anyway, here’s the basic explanation…….

First, we breaks a problem in to parts. Then we solve each part. Finally, we combine solutions of all the parts and get the ultimate result.

We can start solving one part first. Pause it and switch to another task. Later we can again focus on the first. This is the style of concurrent computing.

Another way is simultaneously solving 2 or more parts. This would require a team, at least two people. This is a sub category of concurrency, called parallel computing. In a real computer, we use several processors to achieve parallel computing.

A program we create is usually run as a process. This process can be subdivided in to many parts. Each part is called a thread. This is similar to breaking down the large problem process to smaller pieces called threads.

What is a Thread?

A thread is simply, a sequence of tasks or instructions that are executed by a certain computer program.
For example, consider Java Hello World code.

The main() method is executed in a single thread.

If the processor of your computer has several cores, it can run several threads at once. Each of this core is a unique processor which can handle one thread at once. So, ultimately, it has the capacity to handle several threads. This ability is called multi-threading. So, multi-threading is actually handling several tasks at once.

How to check if my computer processor has several cores?

You can easily check this in Java. Just copy and paste following code snippet and run. If you are new to Java, just check this article on how to configure Java and Eclipse IDE in your PC and run this code.

OUTPUT:

For my PC. I have intel core i5 processor. It has 4 cores inside the processor. So, my PC can handle 4 threads at once.

What happens if my PC has a single core processor?

Of course then only one thread will run at once. Few years ago, there were mostly single core processors. But still, we could play music while we read a web article. That is, single core processors also seem to handle several tasks at once (multi-task). What they actually do is they switch from one task to another very fast so that we see both tasks happen at once.

Now, even when my PC has to handle more than 4 threads, what it does is the same thing a single core processor does. Switching very fast so I don’t feel a lag.

How to create a multi-threaded program using Java?

There are two methods to create threads.

Method 01 : Make a child class of the Class java.lang.Thread

We can extend this Thread Class to make a child class. By making objects of that child class, new threads can be created.

Thread Class has a public method called run(). This method should be overridden and the relevant code for the new thread should be added instead.
Eg:

Note that, java.lang.Thread Class is automatically imported by the system when we create the class ThreadsDemo. So, we actually don’t need to import it explicitly 🙂 Just remove that import statement and see if any error occur. It should not.

The above Class ThreadsDemo is a child class of Thread Class and it overrides the run() method. This run() method is the one which is going to execute in a new thread. That is, it prints a statement informing the number assigned to the variable threadNo.

ThreadNo is assigned a value in the constructor method. So it is compulsory to input an integer as a parameter when creating a new ThreadsDemo object.

Now here’s a Class which create a new thread by using a ThreadsDemo object.

The main() method of ThreadsRun Class is running on a single Thread. We create a ThreadsDemo object thrd0 and assign 0 as the integer parameter. Next statement is an important one.

the start() method will create a new thread in addition to the main() thread. The code inside run() method of thrd0 object will run in that newly created thread.

Now our PC has two tasks to handle.

Either

or

We have only one output panel. So, which one is printed first? Note that, we now have two threads and they are independent of each other. So, any of them can be the winner and print the statement first. So, the order of the output is unpredictable! (Of course, there are certain ways like synchronization and methods like join() (discussed below) to make the thread behaviour predictable, but for now, it is not)

So the output would be
either

or

Run the code several times to verify the two types of outputs.

This uncertainty of the outputs is valid even for single core processors. This is because the processor can switch from one thread to another randomly. So, perhaps it may switch to run() method of the new thread from the main() method of the original thread. Since there’s no any difference in uncertainty, single-core would be no differ than a multi-core. There’s no problem of using any PC to work with Java Threads.

There’s one another thing. What happens if you type

instead of

Try it.

The output now is;

always! No matter how many times you run it. This is because, here, we don’t create a new Thread. Only start() method can create a new thread here. So now, no uncertainty. Only main() thread is available. So, first, the run() is executed as a normal method within the main() thread. Then the print() statement “Running thread 1” below thrd0.run() is executed.

Method 02 : Implement java.lang.Runnable Interface

OK, the second one to make a thread is the Runnable interface. This is also already available one as it is from the package java.lang. So, no need to import again.
It is almost same as the previous one. It also has a run() method which must be implemented in the working Class.

Eg:

I create a new Class called ThreadsRunnable which implements Runnable interface.

The same thing except for the new “implements Runnable” part.

But there’s no any start() method in the Runnable interface. Then how can we start a new Thread?
You have to create a new Thread object for that. Here, the constructor takes a parameter object of a Class which implements Runnable.

See, we have to embed the ThreadsRunnable object inside the Thread object t as a parameter.

Finally, we can start the newly created thread t using start() method as before.

Basic Operations on Threads

  • Thread.sleep(long milliseconds) – Static method. Pauses the thread having this method for a time given in milliseconds as the argument. Note that, you should surround the method with try catch statements.

That is:

This is because, the method Thread.sleep() could throw an InterruptedException. This exception is thrown if the thread is forced to wake up before the end of sleep time. You must definitely handle this exception. This type of exception handling is called mandatory exception handling. The thread’s sleep can be interrupted by the method given below.

  • t.interrupt() – Pause or wake up the thread t. When a thread is sleeping and if we need to wake it up and continue processing, we use this method. Also, if we want to pause the processing of the thread t, we can use this.

 

  • t.join() – Suppose you want to run two threads one after the other. You can do it with this method. If t is a Thread and you call this method in another thread t2. Now t2 sleeps until t finishes the execution and then again starts to run after that. What if t is already dead when you call t.join()? It simply does nothing then. Just moves to the next line of code in t2.

 

  • t.join(long milliseconds) – Similar to previous method but the thread having this method (say, t2) will wake up after the given milliseconds time regardless t is dead or not. If t stops executing before the time given in milliseconds, the thread t2 will wake up and start executing as the previous method.

 

Note that both join() methods mentioned could generate InterruptedException. This means, if we write t.join() inside a thread t2, the thread t2 will freeze after executing this method, until t finishes executing. It is possible to interrupt the sleep of t2 by another thread in this period. So, try catch statements are used to handle what happens to t2 if such interruption occurs.

For example, suppose x1 and x2 are two running threads. These methods are written inside another thread, say, t.

t will sleep after the method call x1.join() until x1 dies and then t starts from where it is paused. That is, the next line x2.join(). Again t freezes until x2 is finished. At any of this time, we could interrupt t’s sleep. Then an InterruptedException object e is generated and is handled by catch() method.

Example of using several threads at once

This example is about counting the number of prime numbers from 1 to 5000000. It seems to be a tedious task but it takes only about a second to do such a calculation using our computers. Here, I use all 4 cores in my PC to perform this calculation. The output shows that the times for the calculations are almost equal. This will change if I create more than 4 threads and time consumed will also increase.

You may want to know the technique used for counting prime numbers. It is used in the method countPrimes() below and is as follows;

consider a number n. We have a list of all prime numbers before n. So in order to check whether n is a prime number, what we normally do is, divide n by each prime number from the list. If n is not divisible by any of them, we consider it also as a prime number.
But it is enough test whether n is divisible only up to square root of n. That means, we no need to go through all the elements of the prime number list, but only the numbers which are less than or equal to square root of n. Why? For more information check this link to see the explanation.

In my laptop, the output was follows;

OUTPUT: