Multi-Tread Programming
1. What is a thread? Explain the difference between a process and a thread, and compare multiprocessing with multithreading.
The main difference between a process and a thread is memory allocation and independence:
A process is an executing program (e.g., web browser, a code editor). Each process has its own private, dedicated memory space. Processes are independent of each other.
A thread exists within a process. Multiple threads in the same process share the same memory space, which allows them to communicate easily but also requires careful management to avoid conflicts (by using synchronization).
A thread is the smallest unit of execution within a process. It's a "lightweight process" that has its own execution path but shares memory and resources with other threads in the same process.
Analogy: A process is like a restaurant. A thread is like a single chef working in that restaurant's kitchen. Multiple chefs (threads) can work in the same kitchen (process), sharing the same ingredients and equipment (memory).
Multiprocessing vs. Multithreading
Feature | Multiprocessing | Multithreading |
---|---|---|
Definition | Running multiple, independent processes simultaneously. | Running multiple threads simultaneously within a single process. |
Memory | Each process has its own separate memory space. | All threads share the same memory space. |
CPU Usage | Can utilize multiple CPUs/cores to run different processes. | Can utilize multiple CPUs/cores to run different threads of the same process. |
Example | Running Google Chrome and Microsoft Word at the same time. | A word processor using one thread for user input, another for spell-checking, and a third for auto-saving. |
2. Explain the Java Thread Model and the life cycle of a thread.
The Java Thread Model is built directly into the language. Every Java program starts with a single thread called the main thread. From this main thread, additional threads can be created and managed.
The java.lang.Thread
class and the Runnable
interface are the foundation of this model, and the Java Virtual Machine (JVM) handles the complex task of scheduling threads.
Thread life cycle is the various states a thread goes through during its existence.
New: The thread object has been created (
new Thread()
), but itsstart()
method has not yet been called. It is not yet alive.Runnable: The thread is ready to execute. It is waiting for the thread scheduler to allocate CPU time. After the
start()
method is called, the thread enters this state. This state encompasses both "ready" and "running."Running: Thread is actively executing.
Blocked / Waiting : The thread is temporarily inactive and not using any CPU time. Waiting for an external event, such as waiting for I/O to complete. Blocked: Waiting for acquiring a lock on an object. Automatically released when resource becomes available.
synchronized(sharedResource){...}
Waiting: Waiting indefinitely for another thread to perform specific action and notify it.
Object.wait()
,Thread.join()
Timed_waiting: Waiting for a limited amount of time.
Object.wait(ms)
,Thread.join(ms)
,Thread.sleep(ms)
Terminated (Dead): The thread has completed its execution because its
run()
method has finished. Once a thread is terminated, it cannot be restarted.
Part B: Creating and Managing Threads
3. What are the two primary ways to create a thread in Java? Provide a simple program that creates and runs multiple threads.
There are two primary ways to create a thread:
Extending the
Thread
Class: Create a class that extends theThread
class and overrides itsrun()
method. Call thestart()
method.Implementing the
Runnable
Interface: Create a class that implements theRunnable
interface and overrides itsrun()
method. Pass an instance of this class to aThread
object's constructor and call thestart()
method of the Tread object. This is preferred because it separates the task (theRunnable
) from the execution mechanism (theThread
) and allows class to extend another class.
class MyRunnable implements Runnable {
private String threadName;
MyRunnable(String name) {
this.threadName = name;
}
public void run() {
for (int i = 1; i <= 3; i++) {
System.out.println(threadName
+ " is running, count: " + i);
}
}
}
class MyThread extends Thread {
public void run() {
for (int i = 1; i <= 3; i++) {
System.out.println("MyThread is running, count: " + i);
}
}
}
public class CreateThreadDemo {
public static void main(String[] args) {
System.out.println("Main thread starting.");
// Create and run a thread by extending Thread
MyThread t1 = new MyThread();
t1.start();
// Create and run multiple threads by implementing Runnable
Thread t2 = new Thread(new MyRunnable("Runnable-A"));
Thread t3 = new Thread(new MyRunnable("Runnable-B"));
t2.start();
t3.start();
System.out.println("Main thread finished.");
}
}
4. Explain the purpose of the isAlive()
and join()
methods for managing thread execution. Provide a code example.
isAlive()
: This method returns aboolean
value indicating whether a thread is currently active. It returnstrue
if the thread has been started but has not yet been terminated. It's useful for monitoring the state of another thread.join()
: This method makes the currently running thread wait until the thread it is called on finishes its execution. It's essential for coordinating tasks, for example, when themain
thread needs to wait for worker threads to complete their work before it can proceed.
class WorkerThread implements Runnable {
public void run() {
System.out.println("Worker thread starting.");
try {
// Simulate some work
for (int i = 1; i <= 5; i++) {
System.out.println("Worker working... step " + i);
Thread.sleep(500);
}
} catch (InterruptedException e) {
System.out.println("Worker thread interrupted.");
}
System.out.println("Worker thread finished.");
}
}
public class JoinAliveDemo {
public static void main(String[] args) {
System.out.println("Main thread starting.");
Thread worker = new Thread(new WorkerThread());
worker.start();
System.out.println("Is worker thread alive? "
+ worker.isAlive());
try {
System.out.println("Main thread waiting for worker to finish...");
worker.join(); // Main thread waits here
} catch (InterruptedException e) {
System.out.println("Main thread interrupted.");
}
System.out.println("Is worker thread alive? "
+ worker.isAlive());
System.out.println("Main thread finished.");
}
}
5. What are thread priorities and how are they set? What is the default priority?
Thread priorities are integer values that act as a suggestion to the thread scheduler about which threads should be given preference for CPU time. Threads with a higher priority are more important and are generally executed before threads with a lower priority.
However, this is not a guarantee, as the exact behavior depends on the underlying operating system.
Priorities are set using the setPriority(int level)
method of the Thread
class. The level ranges from 1 to 10. For readability, Java provides three static constants:
Thread.MIN_PRIORITY
(value 1)Thread.NORM_PRIORITY
(value 5)Thread.MAX_PRIORITY
(value 10)
The default priority for a thread is Thread.NORM_PRIORITY
(5).
A new thread inherits the priority of the thread that created it. Since most threads are created from the main
thread (which has a priority of 5), they also start with a priority of 5.