Java Essentials: Concurrency and Threads
Processes and Threads
A process is a running application with its own memory. A thread is a single path of execution inside that process. Modern CPUs run several cores, and Java lets you use them by creating multiple threads that work in parallel. Concurrency is about writing programs that manage several tasks at once without corrupting shared data.
Runnable task = () -> {
for (int i = 0; i < 5; i++) {
System.out.println("Worker " + i);
}
};
Thread worker = new Thread(task);
worker.start();The Runnable is a lambda that holds the work. Calling start() launches a new thread, and the main thread continues immediately. The print statements from the two threads interleave in an order you cannot predict, because the operating system schedules them.
Sharing Data Safely
When several threads read and write the same field, you can get race conditions. The classic fix is the synchronized keyword, which gives one thread exclusive access to a block or method at a time.
synchronized (counter) {
counter++;
}Only one thread can be inside the block at any moment, so the increment cannot be interrupted halfway. Locking too aggressively slows the program down, so the general advice is to share as little as possible and to keep locked sections short.
Thread Pools with ExecutorService
Creating a thread per task is wasteful for large workloads. An ExecutorService keeps a pool of ready threads and reuses them.
ExecutorService pool = Executors.newFixedThreadPool(4);
pool.submit(() -> System.out.println("Job one"));
pool.submit(() -> System.out.println("Job two"));
pool.shutdown();The pool runs the jobs across four threads and avoids the overhead of starting a new thread for every task. Always shut the pool down when the work is finished so the application can exit cleanly.
Key Points
- A thread is one path of execution; several threads share one process.
- start() launches a thread, and scheduling is controlled by the OS.
- Synchronization prevents race conditions but should be used sparingly.
- ExecutorService reuses a pool of threads for many small tasks.