Chapter 5-Threading
Chapter 5-Threading
Multithreading in Java refers to the concurrent execution of two or more threads in a single
process to perform multiple tasks concurrently. Java provides built-in support for multithreading
through its Thread class and the Runnable interface.
Here's a basic example of creating and running a thread using the Thread class:
System.out.println("MyThread is running");
System.out.println("MyRunnable is running");
}
Multithreading is commonly used in scenarios such as GUI programming, server handling
multiple client requests simultaneously, and performance optimization in CPU-bound tasks.
However, it requires careful synchronization to avoid issues like race conditions and deadlock.
Java provides synchronization mechanisms like synchronized keyword, wait(), notify(), and
notifyAll() methods to manage concurrent access to shared resources safely.
In Java, both threads and processes are mechanisms for concurrent execution, but they differ in
several key aspects:
Threads are managed by the Java Virtual Machine (JVM) and are cheaper to create and manage
compared to processes.
Threads are suitable for tasks that require concurrent execution within the same application, such
as handling multiple client requests in a server application or performing background tasks while
keeping the UI responsive in a GUI application.
Processes: Processes are independent instances of a program that run in their own memory
space.
Each process has its own memory space and resources, including heap, stack, and file
descriptors.
Processes are isolated from each other, and communication between processes typically involves
more overhead, such as inter-process communication (IPC).
Creating and managing processes is more resource-intensive compared to threads. Processes are
suitable for tasks that require isolation, fault tolerance, and scalability, such as running multiple
instances of a server application on different machines or executing different programs
concurrently.
In summary, threads are lightweight units of execution within a single process and are suitable
for concurrent tasks within the same application, while processes are independent instances of a
program with their own memory space and resources, suitable for isolated and concurrent
execution of different programs or parts of a program.
Example
Sure, here are examples demonstrating both threads and processes in Java:
1. Threads Example:
System.out.println("Thread is running");
In this example, ThreadExample extends the Thread class and overrides the run() method to
define the task to be executed by the thread. The main() method creates an instance of
ThreadExample and starts the thread.
1.Process Example:
try {
} catch (Exception e) {
e.printStackTrace();
In this example, the main() method starts a new process using Runtime.getRuntime().exec(),
which launches the Notepad application (notepad.exe). Then, it waits for the process to exit
using process. waitFor(), and finally prints the exit code of the process. These examples illustrate
the creation and execution of threads and processes in Java.
package javaapplication13;
thread1.start();
thread2.start();
thread3.start();
this.name = name;
}
In this example, we define a class MultiThreadExample with the main() method where we create
three instances of MyThread, a subclass of Thread. Each MyThread instance represents a
separate thread with a unique name. Then, we start each thread using the start() method. When
you run this program, you'll see output similar to:
This demonstrates how multiple threads can run concurrently, each executing its own run()
method independently of the others.
// Set priorities
highPriorityThread.setPriority(Thread.MAX_PRIORITY);
lowPriorityThread.setPriority(Thread.MIN_PRIORITY);
// Start threads
highPriorityThread.start();
lowPriorityThread.start();
super(name);
In this example, we create two instances of MyThread, one with high priority
(MAX_PRIORITY) and one with low priority (MIN_PRIORITY). We set the priorities using the
setPriority() method before starting the threads. Then, when the threads run, they print their
names along with their priorities.Please note that the actual behavior might vary depending on
the underlying operating system and the JVM implementation. Additionally, relying heavily on
thread priorities for application logic is generally discouraged due to platform-dependent
behavior and potential for priority inversion problems.
count++;
return count;
2. use synchronized blocks to lock specific sections of code rather than entire methods. This
allows for more fine-grained control over synchronization.
synchronized (this) {
count++;
synchronized (this) {
return count;
}
}
import java.util.concurrent.locks.ReentrantLock;
lock.lock();
try {
count++;
} finally {
lock.unlock();
lock.lock();
try {
return count;
} finally {
lock.unlock();
}
}
4. Volatile Keyword: The volatile keyword ensures that changes to a variable are visible to all
threads. It prevents the compiler and CPU from reordering instructions that involve the volatile
variable.
value = newValue;
return value;
These synchronization mechanisms help prevent issues like data corruption, race conditions, and
inconsistent state in multithreaded Java programs. It's essential to use them judiciously to ensure
thread safety and avoid deadlocks or performance bottlenecks.