Concurrency in Java allows multiple threads to run simultaneously, enabling efficient use of CPU resources. Multithreading is a form of concurrency where multiple threads are executed within a single process.
Key Concepts
-
Thread: A thread is the smallest unit of execution in a program. In Java, you can create a thread by extending the
Thread
class or implementing theRunnable
interface. - Synchronization: Synchronization is used to control the access of multiple threads to shared resources. It helps prevent thread interference and memory consistency errors.
- Executor Framework: The Executor framework provides a higher-level replacement for working with threads directly. It includes thread pools and task scheduling.
-
Locks: Locks provide more extensive locking operations than
synchronized
methods and blocks. Thejava.util.concurrent.locks
package contains several lock implementations. - Atomic Variables: Atomic variables provide a way to perform atomic operations on single variables without using synchronization.
Code Example
Let's look at a simple example to understand how multithreading works in Java:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MultithreadingExample {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(3);
for (int i = 0; i < 5; i++) {
Runnable worker = new WorkerThread("" + i);
executor.execute(worker);
}
executor.shutdown();
while (!executor.isTerminated()) {
}
System.out.println("Finished all threads");
}
}
class WorkerThread implements Runnable {
private String command;
public WorkerThread(String s) {
this.command = s;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " Start. Command = " + command);
processCommand();
System.out.println(Thread.currentThread().getName() + " End.");
}
private void processCommand() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public String toString() {
return this.command;
}
}
Explanation
ExecutorService: We create an ExecutorService with a fixed thread pool of 3 threads. This means that at most 3 threads will run concurrently.
WorkerThread: This class implements the Runnable interface. Each WorkerThread instance represents a task that will be executed by a thread.
Executing Tasks: We submit 5 tasks to the executor. The executor will manage the execution of these tasks using the available threads in the pool.
Shutdown: We call shutdown() on the executor to stop accepting new tasks and wait for the completion of submitted tasks.
Thread Execution: Each WorkerThread prints its start and end messages, and simulates work by sleeping for 5 seconds.
Under the Hood
Thread Creation: When a new thread is created, the JVM allocates a new stack for the thread and initializes it with the necessary data structures.
Context Switching: The JVM's thread scheduler manages the execution of threads. It uses context switching to switch between threads, saving and restoring the state of each thread.
Synchronization: When a thread enters a synchronized block, it acquires a lock on the object. Other threads trying to enter the same block are blocked until the lock is released.
Executor Framework: The Executor framework uses a thread pool to manage a pool of worker threads. This reduces the overhead of creating and destroying threads for each task.
Conclusion
Understanding Java concurrency and multithreading is essential for building high-performance applications. By leveraging the power of threads, you can create applications that efficiently utilize CPU resources and handle multiple tasks simultaneously.
Top comments (0)