
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.
1 2 3 4 5 6 |
public class HelloWorld { public static void main(String[] args){ System.out.println("Hello World!"); } } |
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.
1 2 3 4 5 |
public class ProcessorCores { public static void main(String[] args){ System.out.println(Runtime.getRuntime().availableProcessors()); } } |
OUTPUT:
1 |
4 |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
import java.lang.Thread; //This explicit import is not required because //java.lang.* is automatically imported by the //system. public class ThreadsDemo extends Thread{ private int threadNo; public ThreadsDemo(int num){ threadNo = num; } public void run(){ System.out.println("Running thread "+threadNo); } } |
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.
1 2 3 4 5 6 7 |
public class ThreadsRun { public static void main(String[] args){ ThreadsDemo thrd0 = new ThreadsDemo(0); thrd0.start(); System.out.println("Running thread 1"); } } |
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.
1 |
thrd0.start() |
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
1 |
System.out.println("Running thread 1"); |
or
1 |
System.out.println("Running thread 0"); |
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
1 2 |
Running thread 1 Running thread 0 |
or
1 2 |
Running thread 0 Running thread 1 |
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
1 |
thrd0.run(); |
instead of
1 |
thrd0.start(); |
Try it.
The output now is;
1 2 |
Running thread 0 Running thread 1 |
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.
1 2 3 4 5 6 7 8 9 10 11 |
public class ThreadsRunnable implements Runnable{ private int threadNo; public ThreadsRunnable(int num){ threadNo = num; } public void run(){ System.out.println("Running thread "+threadNo); } } |
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public class ThreadsRun { public static void main(String[] args){ //Create a new ThreadsRunnable Object “demo”. ThreadsRunnable demo = new ThreadsRunnable(0); //pass the object “demo” as a parameter to a //new Thread object t. Thread t = new Thread(demo); //start a new thread by calling t.start(); t.start(); System.out.println("Running thread 1"); } } |
See, we have to embed the ThreadsRunnable object inside the Thread object t as a parameter.
1 |
Thread t = new Thread(demo); |
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:
1 2 3 4 5 6 7 |
try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } |
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.
1 2 3 4 5 6 7 |
try { x1.join(); x2.join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } |
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
import java.util.ArrayList; public class PrimesCounter extends Thread{ private int id; public PrimesCounter(int id){ this.id = id; } public void run(){ long startTime = System.currentTimeMillis(); int count = countPrimes(5000000); long elapsedTime = System.currentTimeMillis()-startTime; System.out.println("The thread "+id+" took "+(elapsedTime/1000.0)+" seconds" + " and counted "+count+" primes"); } private static int countPrimes(int val) { ArrayList primes = new ArrayList(); boolean isPrime=true; for (int i=3;i<=val;i++){ if (isPrime) primes.add(i-1); int j=0; int sqrVal = (int) Math.sqrt(i); sup:for (;j<primes.size();j++){ int div = primes.get(j); if (div>sqrVal){ isPrime=true; break sup; } if (i%(primes.get(j))==0){ isPrime=false; break sup; } } } return primes.size(); } public static void main(String[] args){ int range = 4; PrimesCounter[] worker = new PrimesCounter[range]; long startTime = System.currentTimeMillis(); for(int i=0;i<range;i++){ worker[i] = new PrimesCounter(i+1); worker[i].start(); } for(int i=0;i<range;i++){ try { worker[i].join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } long elapsedTime = System.currentTimeMillis() - startTime; System.out.println("Total elapsed time: " + (elapsedTime/1000.0) + " seconds"); } } |
In my laptop, the output was follows;
OUTPUT:
1 2 3 4 5 |
The thread 3 took 1.43 seconds and counted 348513 primes The thread 4 took 1.49 seconds and counted 348513 primes The thread 1 took 1.563 seconds and counted 348513 primes The thread 2 took 1.604 seconds and counted 348513 primes Total elapsed time: 1.604 seconds |