More than one thing happening inside one program
Every CS105ES program ran one line at a time, in one strict order. A thread is a second, independent line of execution inside the same program, running alongside the first — genuinely new, not a renamed C idea.
After this lesson
You should be able to
- Explain the difference between multitasking and multithreading.
- Create a thread using both the Thread subclass approach and the Runnable interface approach.
- Name the states in a thread's life cycle.
Multitasking vs multithreading
Multitasking is your operating system running several separate programs at once — a browser, a music player, this course, each with its own memory, unaware of the others. Multithreading happens inside one single program: multiple threads share the same memory, the same objects, the same variables. That sharing is both the entire power of threads and the entire source of danger, which the next lesson addresses directly.
A single-threaded C program from CS105ES could only ever do one thing at a time: read input, then compute, then print, strictly in order. A multithreaded Java program can genuinely do more than one of those at once — one thread reading new input while another finishes a computation, for example.
Two ways to create a thread
The first way extends Thread directly, overriding its run() method with the code that should execute on the new thread. The second, generally preferred, way implements Runnable — a simple interface with just one method, run() — and passes that Runnable to a Thread's constructor. Runnable is preferred because your class then has not used up its one extends on Thread, leaving it free to extend something else if it ever needs to.
Either way, calling start() — never calling run() directly — is what actually creates a new thread and schedules it to run. Calling run() directly just calls it as a normal method on the current thread, with no new thread ever created; this is a common and completely silent beginner mistake.
class Greeter extends Thread {
public void run() {
System.out.println("Hello from a thread");
}
}
class GreeterTask implements Runnable {
public void run() {
System.out.println("Hello from a Runnable");
}
}
new Greeter().start();
new Thread(new GreeterTask()).start();The life cycle a thread moves through
A thread is born New, the moment it is constructed but before start() is called. Calling start() moves it to Runnable, meaning it is eligible to run but not necessarily running right now — the JVM's scheduler decides which runnable thread actually gets CPU time at any moment. A running thread can move to Blocked or Waiting when it needs a resource another thread holds, or is deliberately paused. Finally, once run() completes, the thread reaches Terminated, permanently.
Thread priorities, from 1 to 10, are a hint to the scheduler about relative importance — a higher-priority thread is more likely to be chosen when several are runnable at once, but this is only a hint, not a guarantee, since the actual scheduling algorithm is decided by the underlying operating system, not by Java itself.
Try it yourself
Create two threads using the Runnable approach: one that prints "Counting up" three times, and one that prints "Counting down" three times. Start both and observe that the output order is not perfectly predictable.
Need a hint?
Run the program more than once. If the output order changes between runs, you are observing the scheduler making different decisions each time — exactly what the lesson described.
Check the worked solution
The interleaving of "Counting up" and "Counting down" lines varies between runs because the JVM scheduler, not your code, decides the exact order the two runnable threads actually execute in — this unpredictability is the direct, visible consequence of two threads genuinely running independently, not a bug in the program.
Runnable countUp = () -> {
for (int i = 1; i <= 3; i++) System.out.println("Counting up: " + i);
};
Runnable countDown = () -> {
for (int i = 3; i >= 1; i--) System.out.println("Counting down: " + i);
};
new Thread(countUp).start();
new Thread(countDown).start();Quick check
Why does calling run() directly, instead of start(), fail to create a new thread?
Why this lesson exists
Syllabus mapping
Differences between multithreading and multitasking, thread life cycle, creating threads, thread priorities.
Maps to course outcome CO3.