Java Programming · Lesson 14 of 15 · Updated ·
Multithreading in Java — Threads, Runnable, synchronized and Executors
A thread is a path of execution inside a program. Multithreading lets one program do several things at once — download a file while the screen stays responsive, or process thousands of requests on a server.
Key idea: each thread has its own stack, but all threads share the heap. Sharing objects between threads is where bugs come from.
1. Process vs thread
| Process | Thread | |
|---|---|---|
| What | a running program | a path of work inside a process |
| Memory | its own | shares the process's heap |
| Cost to create | high | low |
| Communication | slow (IPC) | fast (shared objects) |
2. Creating threads
public class Main {
public static void main(String[] args) throws InterruptedException {
Runnable task = () -> {
int sum = 0;
for (int i = 1; i <= 100; i++) sum += i;
System.out.println(Thread.currentThread().getName() + " sum=" + sum);
};
Thread t1 = new Thread(task, "worker-1");
t1.start(); // start() runs task in a NEW thread
t1.join(); // wait for it to finish
System.out.println("main done");
}
}
worker-1 sum=5050
main done
- Prefer implementing
Runnable(or a lambda) over extendingThread— your class can still extend something else. - Call
start(), notrun().run()just runs the code in the current thread. join()waits for a thread to finish.
3. Thread life cycle
New → start() → Runnable → (waiting for a lock / sleep / wait) Blocked / Waiting / Timed waiting → back to Runnable → Terminated.
4. Race conditions and synchronized
When two threads update the same variable, updates can be lost. count++ is really three steps (read, add, write).
public class Main {
static int unsafe = 0;
static int safe = 0;
static synchronized void incSafe() { safe++; }
public static void main(String[] args) throws InterruptedException {
Runnable job = () -> {
for (int i = 0; i < 100_000; i++) {
unsafe++; // not protected
incSafe(); // one thread at a time
}
};
Thread a = new Thread(job), b = new Thread(job);
a.start(); b.start();
a.join(); b.join();
System.out.println("safe = " + safe);
System.out.println("unsafe is " + (unsafe == 200_000 ? "lucky this time" : "less than 200000 (lost updates!)"));
}
}
safe is always 200000. unsafe is usually less — the exact number changes every run, which is exactly the problem.
synchronizedlets only one thread at a time into the method (it locks the object/class).- For simple counters,
AtomicIntegeris faster:counter.incrementAndGet().
5. ExecutorService: the modern way
Creating threads by hand is rare in real code. Use a thread pool:
import java.util.*;
import java.util.concurrent.*;
public class Main {
public static void main(String[] args) throws Exception {
ExecutorService pool = Executors.newFixedThreadPool(3);
List<Future<Integer>> results = new ArrayList<>();
for (int n = 1; n <= 5; n++) {
final int x = n;
results.add(pool.submit(() -> x * x)); // a Callable returns a value
}
int total = 0;
for (Future<Integer> f : results) total += f.get(); // waits for each result
pool.shutdown();
System.out.println("sum of squares = " + total);
}
}
sum of squares = 55
6. Deadlock
Thread A holds lock 1 and waits for lock 2; thread B holds lock 2 and waits for lock 1. Both wait forever.
Prevent it by always taking locks in the same order, holding them briefly, or using timeouts (tryLock).
Interview quick answers
- start() vs run()? start() creates a new thread; run() is a normal method call.
- sleep() vs wait()? sleep pauses without releasing locks; wait releases the lock and waits for notify().
- volatile? makes a variable's latest value visible to all threads (no atomicity).
- Thread-safe collections?
ConcurrentHashMap,CopyOnWriteArrayList,BlockingQueue.
Common mistakes
- Calling
run()instead ofstart(). - Sharing mutable data without synchronization.
- Forgetting
shutdown()on an ExecutorService (the program may not exit).
Quick recap
- Threads share the heap, have their own stacks.
- Runnable/lambda +
start();join()to wait. - Protect shared data with
synchronizedor atomic classes. - Use ExecutorService thread pools; avoid deadlock with consistent lock order.