Lab 1 · Thread Creation & Lifecycle
Difficulty: Beginner Time: 30 minutes Topics: Runnable, Callable, Thread states, Thread names
Objectives
- ✅ Create threads using Runnable and Callable
- ✅ Observe thread lifecycle states
- ✅ Work with Futures and results
- ✅ Handle InterruptedException properly
Starter Code
public class Lab01ThreadCreation {
// Exercise 1: Create thread using Runnable
static void exercise1_RunableThread() {
// TODO: Create a Runnable that prints "Hello from Runnable"
// TODO: Create a Thread with that Runnable
// TODO: Call start() and observe the output
}
// Exercise 2: Create thread using Callable
static void exercise2_CallableThread() {
// TODO: Create a Callable<String> that returns "Hello from Callable"
// TODO: Wrap it in an ExecutorService
// TODO: Submit it and get the result using Future.get()
}
// Exercise 3: Observe thread lifecycle states
static void exercise3_ThreadStates() {
final Thread[] threadRef = new Thread[1];
Thread stateObserver = new Thread(() -> {
for (int i = 0; i < 10; i++) {
// TODO: Print thread name and state
// TODO: Sleep between checks
}
});
threadRef[0] = new Thread(() -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
threadRef[0].setName("sleeper");
// TODO: Start both threads and observe state transitions
}
// Exercise 4: Named threads and IDs
static void exercise4_ThreadInfo() {
// TODO: Create 3 named threads
// TODO: Print each thread's name, ID, priority, and state
// TODO: Show that IDs are unique
}
public static void main(String[] args) throws InterruptedException {
System.out.println("=== Lab 01: Thread Creation & Lifecycle ===\n");
exercise1_RunableThread();
System.out.println();
exercise2_CallableThread();
System.out.println();
exercise3_ThreadStates();
System.out.println();
exercise4_ThreadInfo();
}
}
Solution Guide
Exercise 1: Runnable Thread
static void exercise1_RunableThread() {
Runnable task = () -> {
System.out.println("Hello from Runnable");
System.out.println("Running in: " + Thread.currentThread().getName());
};
Thread thread = new Thread(task);
thread.setName("runnable-thread");
thread.start();
try {
thread.join(); // Wait for thread to finish
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
Exercise 2: Callable Thread
static void exercise2_CallableThread() throws InterruptedException, ExecutionException {
ExecutorService executor = Executors.newSingleThreadExecutor();
Callable<String> task = () -> {
Thread.sleep(500);
return "Hello from Callable";
};
Future<String> future = executor.submit(task);
String result = future.get(); // Blocks until result available
System.out.println(result);
executor.shutdown();
}
Exercise 3: Thread States
static void exercise3_ThreadStates() throws InterruptedException {
final Thread[] threadRef = new Thread[1];
Thread stateObserver = new Thread(() -> {
for (int i = 0; i < 10; i++) {
Thread observed = threadRef[0];
if (observed != null) {
System.out.println("Thread: " + observed.getName() +
" | State: " + observed.getState() +
" | Alive: " + observed.isAlive());
}
try {
Thread.sleep(300);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
});
threadRef[0] = new Thread(() -> {
try {
System.out.println("Sleeper thread started");
Thread.sleep(2000);
System.out.println("Sleeper thread ending");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
threadRef[0].setName("sleeper");
stateObserver.start();
threadRef[0].start();
stateObserver.join();
threadRef[0].join();
}
Exercise 4: Thread Info
static void exercise4_ThreadInfo() {
Thread[] threads = new Thread[3];
for (int i = 0; i < 3; i++) {
final int index = i;
threads[i] = new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {}
});
threads[i].setName("worker-" + i);
threads[i].setPriority(Thread.NORM_PRIORITY + (i - 1));
}
for (Thread t : threads) {
t.start();
System.out.println("Thread: " + t.getName() +
" | ID: " + t.threadId() +
" | Priority: " + t.getPriority() +
" | State: " + t.getState());
}
for (Thread t : threads) {
t.join();
}
}
Key Learnings
- Runnable vs Callable: Callable returns a value, Runnable doesn't
- Thread states: NEW → RUNNABLE → (BLOCKED/WAITING/TIMED_WAITING) → TERMINATED
- join(): Wait for a thread to finish (blocks caller)
- Thread naming: Helps debugging multi-threaded applications
- InterruptedException: Must handle or propagate properly
Exercises to Try
- Modify Exercise 1 to create 5 threads and see their interleaved output
- Modify Exercise 2 to submit 10 Callables and collect all results
- Modify Exercise 3 to track state transitions over longer period
- Add Exception Handling to exercise 2 for timeout scenarios