Get free ebooK with 50 must do coding Question for Product Based Companies solved
Fill the details & get ebook over email
Thank You!
We have sent the Ebook on 50 Must Do Coding Questions for Product Based Companies Solved over your email. All the best!

Multithreading Program in Java

Last Updated on July 1, 2023 by Prepbytes

Multithreading is a fundamental concept in Java programming that allows multiple threads to execute concurrently within a program. Threads are lightweight units of execution that enable parallelism and can perform different tasks simultaneously, enhancing the performance and responsiveness of applications. In Java, threads can be created by extending the Thread class or implementing the Runnable interface. Multithreading plays a crucial role in leveraging the power of modern processors with multiple cores and enabling efficient utilization of system resources. However, managing threads and ensuring proper synchronization to avoid conflicts and data inconsistencies are important aspects to consider. In this article, we will explore the basics of multithreading, its benefits, and different techniques for creating and managing threads in Java.

What are Processes and Programs?

We know that the applications that we use on our computers are nothing but programs (or a collection of programs called software). When we open any application, it is these programs that get executed and we get to interact with these programs using another program (or software) called the Operating System.

So, if we think carefully, these programs can be only in 2 stages i.e. under execution and not under execution. It is simply that any application on your computer is either being used or is not being used. So, the programs that are being used or are under execution are called processes.

So, the difference between a program and a process can be understood by the following statement; “A process is a program under execution.”

Now that we have understood the meaning of processes and programs, let us now study what are threads.

What are Threads?

A thread is the smallest unit of a process. Yes, it is very important to understand that a thread is the smallest unit of a process and not a program because a program is not executing and a process is executing. We can understand this with the help of an example. Now please note that this is just an example to illustrate what threads are and not exactly how the threads work.

Consider you are using Microsoft Word on your PC/laptop. In this application, say you are writing an article. So, Microsoft Word is itself a process because it is a program under execution. In Microsoft Word only, there are multiple threads that are running together. For instance, you are typing and the typed content is being shown on the screen. This can be considered as a thread. Another thread can be spell check while you are typing. Auto-saving is another thread running simultaneously.

So, this is what threads are. Threads are the smallest parts or units of processes and threads combine to form a process. Now, let us study whether the threads and processes are interdependent on each other or on other processes and threads or not.

Threads and Processes Interdependence

The different processes running on a machine are actually independent of each other. This is because the running of the Microsoft Word application has nothing to do with the running of the Chrome Browser, for instance.

However, there might be some part of the code that might be common in the 2 processes. This common code part that deals with either a shared variable between the processes or a shared resource is called a Critical Section.

However, this Critical Section is dealt with by the Operating System using Process Synchronization.

So, to keep it short, the processes are independent of each other in general and if they depend upon each other because of the critical section problem, it is dealt with using process synchronization.

The threads however are dependent on each other. This is because threads are smaller units of the same process. Hence, they depend on each other. So, the interdependence of threads is also dealt with by a concept called thread synchronization.

Now that we have understood the thread and process concepts in detail, let us study the need for multithreading.

What is the need for Multithreading?

The simple reason for the need for multithreading is to increase performance. Today, processors come with multiple cores and we can run different threads on these different cores simultaneously to improve performance and increase the speed of execution.

Fun Fact: You can know the number of cores in your CPU if you are a windows user by going to the task manager and clicking the performance tab.

Now that we have understood the meaning of processes, threads, and the need for multithreading, let us now try to write a multithreading program in Java.

Multithreading Program in Java – Using the Thread Class

Consider the following program written below.

Hello-Hi Program in Java

class Hello {
    public void run() {
        for(int i=1;i<=5;i++) {
            System.out.println("Hello");
        }
    }
}
class Hi {
    public void run() {
        for(int i=1;i<=5;i++) {
            System.out.println("Hi");
        }
    }
}
public class Main {
    public static void main(String[] args) {
        Hello t1 = new Hello();
        Hi t2 = new Hi();
        t1.run();
        t2.run();
    }
}
 

What do you think is the output of this program? Well, we have first called the run() method of the Hello Class, and then we call the run() method of the Hi() class. The run() method of the Hello class is supposed to print “Hello” 5 times and the run() method of the Hi class is supposed to print “Hi” 5 times. So, the output should be 5 times “Hello” and then 5 times “Hi” as shown below.

This is the correct output. So, from this, we need to understand one concept called the main thread. Actually, every program in Java will run from top to bottom and left to right in fashion. So, if the run() method of the Hello class is called first, it will complete its execution first, and then only the run() method of the Hi class will start its execution.

If we want to make Hello and Hi print randomly and execute simultaneously, we need to create 2 threads of Hello and Hi respectively. This can be done by extending (inheriting) the Thread Class in Java.

The following program shows a multithreading program in Java using the Thread Class.

Multithreading Program in Java – Using Thread Class

class Hello extends Thread {
    public void run() {
        for(int i=1;i<=200;i++) {
            System.out.println("Hello");
        }
    }
}
class Hi extends Thread{
    public void run() {
        for(int i=1;i<=200;i++) {
            System.out.println("Hi");
        }
    }
}
public class Main {
    public static void main(String[] args) {
        Hello t1 = new Hello();
        Hi t2 = new Hi();
        t1.start();
        t2.start();
    }
}

Output:

We have shown a part of the output in the above image. It shows that a number of times “Hello” got printed and then suddenly in between “Hi” got printed a few times, then again “Hello” gets printed. Let us understand the code written above.

Explanation: The code above has 2 classes Hello and Hi and they both extend the Thread Class. The Thread class is a Class in java used to implement multithreading. The Thread class contains a method run() that needs to be overridden by the class extending the thread class to implement multithreading. So, the Hello and the Hi Classes have overridden the run() method respectively. Now, in the main() method, we create the objects of these classes and we have strangely not called the run() method. In fact, instead of the run() method, we have called a start() method that is not even defined in both classes.

This is actually how we implement multithreading in java. When the start() method is called (which is there in the Thread Class), it, in turn, calls the run() method of that particular Child class i.e. the Hi and Hello classes respectively in this case. So, this start() method invoking depicts that we have started a new thread. So, these 2 threads are started simultaneously and we are able to see in the output that they are getting executed together.

Obviously, here we are not able to get alternate “Hello” and “Hi” printed because the processor (CPU) is executing these threads with some context switching that we don’t know.

However, we can control it, we will see that in some other lesson. Let us now look at one more method to implement multithreading in Java.

Multithreading Program in Java – Using Runnable Interface

So, we saw the above multithreading program in Java using the Thread Class in Java. Let us implement the same using the Runnable Interface as shown below.

Multithreading Program in Java – Using Runnable Interface

class Hello implements Runnable {
    public void run() {
        for(int i=1;i<=200;i++) {
            System.out.println("Hello");
        }
    }
}
class Hi implements Runnable {
    public void run() {
        for(int i=1;i<=200;i++) {
            System.out.println("Hi");
        }
    }
}
public class Main {
    public static void main(String[] args) {
        Thread t1 = new Thread(new Hello());
        Thread t2 = new Thread(new Hi());
        t1.start();
        t2.start();
    }
}

Output:

So, the above image shows a part of the entire output. We can see that both the threads are being executed simultaneously. Let us now understand the above program in detail.

Explanation: Instead of extending the Thread class, we have now implemented the Runnable Interface. So, the concept of overriding the run() method remains the same. Also, we are calling the start() method in the main method() so that remains the same too. However, now we are not creating the objects of Hello and Hi classes. We have created the objcects of Thread class and have passed the objects of Hello and Hi classes as a parameter to the Thread() constructor. Again, calling the start() method calls the run() method of that particular class and we get our output.

Conclusion
In summary, this article provided an overview of multithreading in Java. It explained the concepts of processes and threads, highlighting their interdependence. The need for multithreading to improve performance was discussed. The article presented two methods to implement multithreading using the Thread class and the Runnable interface. Overall, it serves as a concise introduction to multithreading in Java, offering readers a basic understanding of the topic.

Frequently Asked Questions (FAQs)

Q1: What is the difference between a process and a thread in Java?
A process is an executing program, while a thread is a single unit of execution within a process. A process can have multiple threads running concurrently, each performing a different task, whereas a thread represents a single flow of execution within a process.

Q2: Why is multithreading important in Java?
Multithreading allows concurrent execution of multiple threads within a program, leading to improved performance and responsiveness. It enables efficient utilization of resources, such as multiple cores in modern processors, and enables handling multiple tasks simultaneously.

Q3: How can I create a thread in Java?
In Java, you can create a thread by either extending the Thread class or implementing the Runnable interface. By extending the Thread class, you override the run() method to define the thread’s behavior. Alternatively, by implementing the Runnable interface, you define the thread’s logic in the run() method and pass it as a parameter to a Thread object.

Q4: What is thread synchronization in Java?
Thread synchronization is a mechanism in Java that ensures proper coordination and orderly execution of multiple threads. It helps prevent issues such as race conditions and data inconsistencies that may arise when multiple threads access shared resources simultaneously. Techniques like synchronized blocks and locks are used to achieve thread synchronization.

Q5: How can I handle exceptions in multithreaded Java programs?
Exception handling in multithreaded Java programs involves catching and handling exceptions that occur within individual threads. Each thread should have its own exception handling mechanism to handle exceptions within its scope. Additionally, uncaught exceptions can be handled globally using the Thread.setDefaultUncaughtExceptionHandler() method to provide a default exception handler for all threads.

Leave a Reply

Your email address will not be published. Required fields are marked *