Thread
ClassRunnable
InterfaceA thread is a lightweight subprocess, the smallest unit of processing that can be scheduled by the operating system. In Java, a thread is represented by the Thread
class. Each Java program has at least one thread, the main thread, which is created by the Java Virtual Machine (JVM) when the program starts.
A Java thread can be in one of the following states: NEW
, RUNNABLE
, BLOCKED
, WAITING
, TIMED_WAITING
, and TERMINATED
. Understanding these states is crucial for debugging and optimizing multithreaded applications.
Thread
ClassThe first way to create a thread in Java is by extending the Thread
class. Here is a simple example:
class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + ": " + i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class ThreadExample {
public static void main(String[] args) {
MyThread thread1 = new MyThread();
MyThread thread2 = new MyThread();
thread1.start();
thread2.start();
}
}
In this example, we create a new class MyThread
that extends the Thread
class and overrides the run()
method. The run()
method contains the code that will be executed by the thread. We then create two instances of MyThread
and start them using the start()
method.
Runnable
InterfaceThe second way to create a thread is by implementing the Runnable
interface. This is generally preferred because Java does not support multiple inheritance, and a class can implement multiple interfaces.
class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + ": " + i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class RunnableExample {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread thread1 = new Thread(myRunnable, "Thread 1");
Thread thread2 = new Thread(myRunnable, "Thread 2");
thread1.start();
thread2.start();
}
}
In this example, we create a class MyRunnable
that implements the Runnable
interface and overrides the run()
method. We then create two Thread
objects, passing the MyRunnable
instance to their constructors, and start the threads.
Web servers need to handle multiple client requests simultaneously. Multithreading can be used to achieve this. Here is a simple example of a multithreaded web server:
import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
class RequestHandler implements Runnable {
private Socket socket;
public RequestHandler(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try (OutputStream out = socket.getOutputStream()) {
String response = "HTTP/1.1 200 OK\r\nContent - Type: text/html\r\n\r\n<html><body>Hello, World!</body></html>";
out.write(response.getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public class WebServer {
public static void main(String[] args) {
try (ServerSocket serverSocket = new ServerSocket(8080)) {
System.out.println("Server started on port 8080");
while (true) {
Socket socket = serverSocket.accept();
Thread thread = new Thread(new RequestHandler(socket));
thread.start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
In this example, the WebServer
class listens for incoming client requests on port 8080. When a request is received, it creates a new RequestHandler
instance and starts a new thread to handle the request.
Multimedia applications often need to play audio and video streams concurrently. For example, in a video player, one thread can be responsible for decoding and rendering the video frames, while another thread can handle the audio playback.
Thread safety is a crucial aspect of multithreaded programming. A class is thread - safe if it behaves correctly when accessed by multiple threads concurrently. Java provides several mechanisms to ensure thread safety, such as synchronized
methods and blocks, ReentrantLock
, and atomic variables.
class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public int getCount() {
return count;
}
}
public class ThreadSafetyExample {
public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
});
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
});
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println("Count: " + counter.getCount());
}
}
In this example, the increment()
method of the Counter
class is declared as synchronized
, which ensures that only one thread can execute this method at a time, preventing race conditions.
Proper resource management is essential in multithreaded applications. Threads should release resources such as file handles, network connections, and database connections when they are no longer needed. This can be achieved using try - with - resources
statements and the finally
block.
Java multithreading is a powerful feature that can significantly improve the performance and responsiveness of real - world applications. By understanding the fundamental concepts, using the appropriate usage methods, following common practices, and adhering to best practices, developers can create robust and efficient multithreaded applications. However, multithreaded programming also introduces challenges such as thread safety and resource management, which need to be carefully addressed.