Thread
class) that will execute this code.Thread
class,Runnable
interface.run()
method, which contains the code to be executed by the thread.Thread
class implements the Runnable
interface, which defines the run()
method.class MyThread extends Thread { @Override public void run() { System.out.println( "Thread: " + Thread.currentThread().getName() + " (ID: " + Thread.currentThread().threadId() + ")"); } }
class MyRunnable implements Runnable { @Override public void run() { System.out.println( "Runnable: " + Thread.currentThread().getName() + " (ID: " + Thread.currentThread().threadId() + ")"); } }It is strongly recommended to implement the
Runnable
interface rather than extend the Thread
class,
because it provides a clear separation of concerns between the thread (the execution context) and the task (the code to be executed).Runnable
allows your class to extend another class (since Java doesn't support multiple inheritance).
It's also easier to use with thread pools and executor services.Thread
class is reserved for cases where you want to modify or enhance the thread's behavior itself.Thread
class (or a subclass of it).run()
method).Thread
object is created; the actual thread of execution has not yet started.Thread
class:public class MainClass { public static void main(String[] args) { // Thread is created but not started yet Thread myThread = new MyThread(); } }
Runnable
interface:public class MainClass { public static void main(String[] args) { Runnable myRunnable = new MyRunnable(); Thread myThread1 = new Thread(myRunnable); // Giving a custom name to the thread Thread myThread2 = new Thread(myRunnable, "MyCustomThreadName"); } }
Runnable
interface when instantiating the thread:
public class MainClass { public static void main(String[] args) { // Using anonymous class Thread myThread1 = new Thread(new Runnable() { @Override public void run() { System.out.println("Anonymous Runnable: " + Thread.currentThread().getName() + " (ID: " + Thread.currentThread().threadId() + ")"); } }); // Using lambda expression Thread myThread2 = new Thread(() -> { System.out.println("Lambda Runnable: " + Thread.currentThread().getName() + " (ID: " + Thread.currentThread().threadId() + ")"); }); } }
run()
method.start()
method on the thread instance.public class MainClass { public static void main(String[] args) { // Using anonymous class Runnable myRunnable1 = new Runnable() { @Override public void run() { System.out.println("Anonymous Runnable: " + Thread.currentThread().getName() + " (" + Thread.currentThread().threadId() + ")"); } }; // Using lambda expression Runnable myRunnable2 = () -> { System.out.println("Lambda Runnable: " + Thread.currentThread().getName() + " (" + Thread.currentThread().threadId() + ")"); }; Thread myThread1 = new Thread(myRunnable1, "AnonymousRunnable"); Thread myThread2 = new Thread(myRunnable2, "LambdaRunnable"); myThread1.start(); // This starts the new thread of execution myThread2.start(); // This starts the new thread of execution } }Notes:
run()
directly vs. start()
:run()
method. However, calling run()
directly will execute the code
in the current thread, not in a new thread of execution.public class MainClass { public static void main(String[] args) { Runnable myRunnable = () -> { System.out.println("Runnable: " + Thread.currentThread().getName() + " (ID: " + Thread.currentThread().threadId() + ")"); }; Thread myThread = new Thread(myRunnable, "myFirstThread"); myThread.start(); // Creates new thread, executes run() in that thread myThread.run(); // Executes run() in current thread (main thread) } }Typical output:
Runnable: myFirstThread (ID: 19) Runnable: main (ID: 1)As you can see, the
run()
method was executed by the current thread (main thread with ID 1).start()
creates a new thread of execution, which internally calls the run()
method.start()
method can only be called once:start()
method can launch a new thread of execution.IllegalThreadStateException
.public class MainClass { public static void main(String[] args) { Runnable myRunnable = () -> { System.out.println("Runnable: " + Thread.currentThread().getName() + " (ID: " + Thread.currentThread().threadId() + ")"); }; Thread myThread = new Thread(myRunnable, "myFirstThread"); myThread.start(); // First call - works fine myThread.start(); // Second call - throws exception } }Output:
Runnable: myFirstThread (9) Exception in thread "main" java.lang.IllegalThreadStateException at java.base/java.lang.Thread.start(Thread.java:790) at MainClass.main(MainClass.java:12)
start()
on each of them.public class MainClass { public static void main(String[] args) { Runnable myRunnable = () -> { System.out.println("Runnable: " + Thread.currentThread().getName() + " (ID: " + Thread.currentThread().threadId() + ")"); }; Thread myThread1 = new Thread(myRunnable, "myFirstThread"); Thread myThread2 = new Thread(myRunnable, "mySecondThread"); Thread myThread3 = new Thread(myRunnable, "myThirdThread"); myThread1.start(); myThread2.start(); myThread3.start(); } }Typical output:
Runnable: myThirdThread (ID: 21) Runnable: myFirstThread (ID: 19) Runnable: mySecondThread (ID: 20)
setPriority()
, but the actual scheduling behavior is platform-dependent.join()
,
so one thread will wait for another to complete before continuing execution.Thread.State
enum defines these states:start()
has not yet been called.start()
method has been called, and the thread is either running or ready to run.Object.wait()
, Thread.join()
).Thread.sleep()
, Object.wait(timeout)
).run()
method has finished executing, either normally or due to an exception.public class MainClass { public static void main(String[] args) { Runnable myRunnable = () -> { System.out.println("(1) Inside run(): " + Thread.currentThread().getState()); // RUNNABLE try { Thread.sleep(1000); // Thread will be in TIMED_WAITING state } catch (InterruptedException e) { Thread.currentThread().interrupt(); // Restore interrupt status System.err.println("Thread was interrupted"); } }; Thread myThread = new Thread(myRunnable, "myTestThread"); System.out.println("(2) After creation: " + myThread.getState()); // NEW myThread.start(); System.out.println("(3) After start(): " + myThread.getState()); // RUNNABLE try { Thread.sleep(500); // Let the other thread start sleeping } catch (InterruptedException e) { Thread.currentThread().interrupt(); } System.out.println("(4) While sleeping: " + myThread.getState()); // TIMED_WAITING try { myThread.join(); // Wait for the thread to complete } catch (InterruptedException e) { Thread.currentThread().interrupt(); } System.out.println("(5) After completion: " + myThread.getState()); // TERMINATED } }Typical output:
(2) After creation: NEW (1) Inside run(): RUNNABLE (3) After start(): RUNNABLE (4) While sleeping: TIMED_WAITING (5) After completion: TERMINATEDNote: The exact output order may vary due to the concurrent nature of threads, but the states will follow the expected lifecycle.