Home/Java/Thread Creation in Java

Thread Creation in Java

Learn how to create and start threads in Java programs

Think of threads like workers in a restaurant! Just like a restaurant can have multiple chefs cooking different dishes at the same time, your Java program can have multiple threads doing different tasks at the same time. Instead of waiting for one task to finish before starting another, threads let your program do many things at once!

The Problem Without Threads

Imagine you're running a pizza shop. If you only have one worker, they have to take an order, make the pizza, bake it, and serve it before helping the next customer. Customers have to wait a long time! Let's see this problem in code:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
// Without threads - everything happens one after another
public class SingleThreadExample {
public static void main(String[] args) {
System.out.println("Starting pizza shop...");
// Task 1: Take order (takes 2 seconds)
takeOrder("Margherita Pizza");
// Task 2: Make pizza (takes 5 seconds)
makePizza("Margherita Pizza");
// Task 3: Bake pizza (takes 10 seconds)
bakePizza("Margherita Pizza");
System.out.println("Pizza shop closed for the day!");
}
static void takeOrder(String pizzaType) {
System.out.println("Taking order for: " + pizzaType);
try {
Thread.sleep(2000); // Simulate 2 seconds
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Order taken!");
}
static void makePizza(String pizzaType) {
System.out.println("Making: " + pizzaType);
try {
Thread.sleep(5000); // Simulate 5 seconds
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Pizza made!");
}
static void bakePizza(String pizzaType) {
System.out.println("Baking: " + pizzaType);
try {
Thread.sleep(10000); // Simulate 10 seconds
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Pizza ready!");
}
}
/* Output:
Starting pizza shop...
Taking order for: Margherita Pizza
Order taken!
Making: Margherita Pizza
Pizza made!
Baking: Margherita Pizza
Pizza ready!
Pizza shop closed for the day!
Total time: 17 seconds (2 + 5 + 10)
*/

Creating Threads by Extending Thread Class

The first way to create a thread is by extending the Thread class. It's like hiring a specialized worker who knows exactly what to do! You create a class that extends Thread and override the run() method with the task you want the thread to perform.

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// Creating a thread by extending Thread class
class PizzaMaker extends Thread {
private String pizzaType;
public PizzaMaker(String pizzaType) {
this.pizzaType = pizzaType;
}
// The run() method contains the code that will run in this thread
@Override
public void run() {
System.out.println(pizzaType + " - Starting to make pizza...");
try {
Thread.sleep(5000); // Takes 5 seconds to make
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(pizzaType + " - Pizza is ready!");
}
}
public class ThreadExtendExample {
public static void main(String[] args) {
System.out.println("Pizza shop opening...");
// Create three pizza maker threads
PizzaMaker maker1 = new PizzaMaker("Margherita");
PizzaMaker maker2 = new PizzaMaker("Pepperoni");
PizzaMaker maker3 = new PizzaMaker("Hawaiian");
// Start all threads - they all work at the same time!
maker1.start();
maker2.start();
maker3.start();
System.out.println("All pizza makers are working!");
}
}
/* Output (approximately):
Pizza shop opening...
All pizza makers are working!
Margherita - Starting to make pizza...
Pepperoni - Starting to make pizza...
Hawaiian - Starting to make pizza...
Margherita - Pizza is ready!
Pepperoni - Pizza is ready!
Hawaiian - Pizza is ready!
Total time: ~5 seconds (all work in parallel!)
*/

Creating Threads by Implementing Runnable

The second way is to implement the Runnable interface. This is like creating a job description that any worker can follow! This approach is more flexible because Java doesn't support multiple inheritance, but a class can implement multiple interfaces.

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
// Creating a thread by implementing Runnable
class OrderTaker implements Runnable {
private String customerName;
public OrderTaker(String customerName) {
this.customerName = customerName;
}
@Override
public void run() {
System.out.println("Taking order from: " + customerName);
try {
Thread.sleep(2000); // Takes 2 seconds
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Order from " + customerName + " is complete!");
}
}
public class RunnableExample {
public static void main(String[] args) {
System.out.println("Restaurant opening...");
// Create Runnable tasks
OrderTaker task1 = new OrderTaker("Alice");
OrderTaker task2 = new OrderTaker("Bob");
OrderTaker task3 = new OrderTaker("Charlie");
// Create Thread objects and pass the Runnable tasks
Thread waiter1 = new Thread(task1);
Thread waiter2 = new Thread(task2);
Thread waiter3 = new Thread(task3);
// Start all threads
waiter1.start();
waiter2.start();
waiter3.start();
System.out.println("All waiters are serving customers!");
}
}
/* Output:
Restaurant opening...
All waiters are serving customers!
Taking order from: Alice
Taking order from: Bob
Taking order from: Charlie
Order from Alice is complete!
Order from Bob is complete!
Order from Charlie is complete!
*/

Using Lambda Expressions (Modern Way)

With Java 8+, we can use lambda expressions to create threads more easily! Since Runnable is a functional interface (has only one method), we can write it as a short, clean lambda expression. It's like giving quick instructions to a worker!

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// Creating threads with lambda expressions
public class LambdaThreadExample {
public static void main(String[] args) {
System.out.println("Starting tasks...");
// Method 1: Lambda with Runnable
Thread downloadThread = new Thread(() -> {
System.out.println("Downloading file...");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Download complete!");
});
// Method 2: Even shorter lambda
Thread uploadThread = new Thread(() -> {
System.out.println("Uploading data...");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Upload complete!");
});
// Method 3: Inline lambda with start()
new Thread(() -> {
System.out.println("Processing data...");
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Processing complete!");
}).start();
downloadThread.start();
uploadThread.start();
System.out.println("All tasks running in parallel!");
}
}
/* Output:
Starting tasks...
All tasks running in parallel!
Downloading file...
Uploading data...
Processing data...
Upload complete!
Download complete!
Processing complete!
*/

Thread Lifecycle and Important Methods

Threads have a lifecycle - they're born, they work, and they finish. Understanding this lifecycle helps you control your threads better. Let's explore important thread methods like start(), sleep(), join(), and getName()!

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
// Understanding thread lifecycle and methods
public class ThreadLifecycleExample {
public static void main(String[] args) {
System.out.println("Main thread: " + Thread.currentThread().getName());
// Create a worker thread
Thread worker = new Thread(() -> {
System.out.println("Worker started: " + Thread.currentThread().getName());
for (int i = 1; i <= 5; i++) {
System.out.println("Working on task " + i + "/5");
try {
Thread.sleep(1000); // Sleep for 1 second
} catch (InterruptedException e) {
System.out.println("Worker was interrupted!");
return;
}
}
System.out.println("Worker finished all tasks!");
}, "MyWorkerThread");
// Check thread state before starting
System.out.println("State before start: " + worker.getState()); // NEW
// Start the thread
worker.start();
System.out.println("State after start: " + worker.getState()); // RUNNABLE
// Main thread continues running
System.out.println("Main thread continues...");
try {
// Wait for worker to finish
System.out.println("Main thread waiting for worker...");
worker.join(); // Main thread waits here
System.out.println("State after join: " + worker.getState()); // TERMINATED
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Main thread finished!");
}
}
/* Output:
Main thread: main
State before start: NEW
State after start: RUNNABLE
Main thread continues...
Main thread waiting for worker...
Worker started: MyWorkerThread
Working on task 1/5
Working on task 2/5
Working on task 3/5
Working on task 4/5
Working on task 5/5
Worker finished all tasks!
State after join: TERMINATED
Main thread finished!
*/

Real-World Example: Download Manager

Let's build a practical download manager that downloads multiple files at the same time! This shows how threads make programs faster and more efficient. Each file downloads in its own thread, so they all download simultaneously instead of one after another.

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
// Real-world example: Multi-threaded download manager
class FileDownloader implements Runnable {
private String fileName;
private int fileSizeMB;
public FileDownloader(String fileName, int fileSizeMB) {
this.fileName = fileName;
this.fileSizeMB = fileSizeMB;
}
@Override
public void run() {
System.out.println("Starting download: " + fileName + " (" + fileSizeMB + "MB)");
// Simulate download progress
for (int i = 1; i <= fileSizeMB; i++) {
try {
Thread.sleep(500); // Each MB takes 0.5 seconds
int percentage = (i * 100) / fileSizeMB;
System.out.println(fileName + ": " + percentage + "% complete");
} catch (InterruptedException e) {
System.out.println(fileName + ": Download interrupted!");
return;
}
}
System.out.println(fileName + ": Download COMPLETE! ✓");
}
}
public class DownloadManager {
public static void main(String[] args) {
System.out.println("=== Download Manager Started ===");
long startTime = System.currentTimeMillis();
// Create download tasks
FileDownloader video = new FileDownloader("movie.mp4", 5);
FileDownloader music = new FileDownloader("song.mp3", 3);
FileDownloader document = new FileDownloader("report.pdf", 2);
// Create threads
Thread videoThread = new Thread(video);
Thread musicThread = new Thread(music);
Thread docThread = new Thread(document);
// Start all downloads
videoThread.start();
musicThread.start();
docThread.start();
try {
// Wait for all downloads to complete
videoThread.join();
musicThread.join();
docThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
long endTime = System.currentTimeMillis();
double totalSeconds = (endTime - startTime) / 1000.0;
System.out.println("=== All Downloads Complete! ===");
System.out.println("Total time: " + totalSeconds + " seconds");
System.out.println("(Without threads, it would take: " + ((5+3+2) * 0.5) + " seconds)");
}
}
/* Output:
=== Download Manager Started ===
Starting download: movie.mp4 (5MB)
Starting download: song.mp3 (3MB)
Starting download: report.pdf (2MB)
movie.mp4: 20% complete
song.mp3: 33% complete
report.pdf: 50% complete
movie.mp4: 40% complete
song.mp3: 66% complete
report.pdf: 100% complete
report.pdf: Download COMPLETE!
movie.mp4: 60% complete
song.mp3: 100% complete
song.mp3: Download COMPLETE!
movie.mp4: 80% complete
movie.mp4: 100% complete
movie.mp4: Download COMPLETE!
=== All Downloads Complete! ===
Total time: 2.5 seconds
(Without threads, it would take: 5.0 seconds)
*/

Key Concepts

Thread vs Runnable

Extend Thread when you need thread-specific functionality. Implement Runnable when you want to separate the task from the thread mechanism, or when your class already extends another class.

start() vs run()

Always use start() to begin a thread, not run()! Calling start() creates a new thread and executes run() in it. Calling run() directly executes the method in the current thread, defeating the purpose of multithreading.

Thread.sleep()

Makes the current thread pause for a specified time. Useful for simulating delays, but remember it throws InterruptedException which must be handled.

join() Method

Waits for a thread to finish before continuing. Like waiting for a worker to complete their task before moving on. Essential for coordinating thread completion.

Best Practices

  • Prefer Runnable over extending Thread - it's more flexible and follows better OOP principles
  • Always give your threads meaningful names using the Thread constructor or setName() for easier debugging
  • Handle InterruptedException properly - don't just catch and ignore it
  • Use join() when you need to wait for threads to complete before proceeding
  • Don't call run() directly - always use start() to actually create a new thread
  • Keep the run() method focused - break complex tasks into smaller helper methods

Common Mistakes to Avoid

  • Calling run() instead of start() - this doesn't create a new thread!
  • Calling start() multiple times on the same thread - it will throw IllegalThreadStateException
  • Forgetting to handle InterruptedException when using Thread.sleep()
  • Creating too many threads - each thread consumes system resources
  • Not using join() when you need to wait for thread completion
  • Ignoring thread names - unnamed threads make debugging much harder

Interview Tips

  • 💡Know both ways to create threads (extends Thread vs implements Runnable) and when to use each
  • 💡Understand the difference between start() and run() - this is a common interview question
  • 💡Be able to explain the thread lifecycle: NEW → RUNNABLE → RUNNING → TERMINATED
  • 💡Know what join() does and why it's important for thread coordination
  • 💡Understand that Thread.sleep() doesn't release locks (unlike wait())
  • 💡Be prepared to write code showing both thread creation approaches