How to create, start, pause, stop and join threads in Java programming language.
package dev.bitek.multithreading.example1;
public class Multithreading {
public static void main(String[] args) {
Thread thread = new Thread();
thread.start();
}
}
This code starts new thread along side the main Java thread that does nothing.
To make the new thread do something we can do it in four ways.
package dev.bitek.multithreading.example2;
public class ThreadExample2 {
public static class MyThread extends Thread {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
System.out.println("[MyThread] Start");
// do hard work here
System.out.println("[MyThread] Finish");
}
}
public static void main(String[] args) {
MyThread thread = new MyThread("Thread subclass");
thread.start();
}
}
Output:
Thread subclass
[MyThread] Start
[MyThread] Finish
package dev.bitek.multithreading.example3;
public class ThreadExample3 {
public static class MyTask implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
System.out.println("[MyTask] Start");
// do hard work here
System.out.println("[MyTask] Finish");
}
}
public static void main(String[] args) {
Thread thread = new Thread(new MyTask(), "Thread running MyTask as runnable");
thread.start();
}
}
Output:
Thread running MyTask as runnable
[MyTask] Start
[MyTask] Finish
package dev.bitek.multithreading.example4;
public class ThreadExample4 {
public static void main(String[] args) {
Runnable myTask = new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
System.out.println("[MyTask] Start");
// do hard work here
System.out.println("[MyTask] Finish");
}
};
Thread thread = new Thread(myTask, "Thread running MyTask from anonymous class");
thread.start();
}
}
Output:
Thread running MyTask from anonymous class
[MyTask] Start
[MyTask] Finish
package dev.bitek.multithreading.example5;
public class ThreadExample5 {
public static void main(String[] args) {
Runnable myTask = () -> {
System.out.println(Thread.currentThread().getName());
System.out.println("[MyTask] Start");
// do hard work here
System.out.println("[MyTask] Finish");
};
Thread thread = new Thread(myTask, "Thread running MyTask from lambda func");
thread.start();
}
}
Output:
Thread running MyTask from lambda func
[MyTask] Start
[MyTask] Finish
package dev.bitek.multithreading.example6;
public class ThreadExample6 {
public static void main(String[] args) {
Runnable myTask = () -> {
String threadName = Thread.currentThread().getName();
long threadID = Thread.currentThread().getId();
System.out.println(threadName);
System.out.printf("[MyTask %d] Start\n", threadID);
// do hard work here
System.out.printf("[MyTask %d] Finish\n", threadID);
};
Thread thread1 = new Thread(myTask, "Thread running MyTask 1");
thread1.start();
Thread thread2 = new Thread(myTask, "Thread running MyTask 2");
thread2.start();
}
}
Output:
Thread running MyTask 1
Thread running MyTask 2
[MyTask 15] Start
[MyTask 15] Finish
[MyTask 14] Start
[MyTask 14] Finish
or
Thread running MyTask 2
Thread running MyTask 1
[MyTask 14] Start
[MyTask 14] Finish
[MyTask 15] Start
[MyTask 15] Finish
Because we have no guarantee in which order the CPU will schedule the two runnables to execute,
we cannot determine apriori which thread will run first.
package dev.bitek.multithreading.example7;
public class ThreadExample7 {
public static void main(String[] args) {
Runnable myTask = () -> {
String threadName = Thread.currentThread().getName();
System.out.printf("[%s] Started\n", threadName)
try {
Thread.sleep(5000)
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("[%s] Finished\n", threadName)
};
Thread sleepyThread = new Thread(myTask, "Thread running MyTask");
sleepyThread.start();
}
}
Output:
[Thread running MyTask] Started
[Thread running MyTask] Finished ---> (after 5 seconds of waiting)
To be able to stop a thread from running we need to manage the stopping operation ourselves.
We can achieve this using the synchronized
keyword on the methods we need exclusive access guarantees.
In this way, a synchronized method can be called by only one thread at the same time on the same Runnable instance.
package dev.bitek.multithreading.example8;
public class ThreadExample8 {
public static class StoppableTask implements Runnable {
private boolean stopped = false;
public synchronized void stop() {
this.stopped = true;
}
public synchronized boolean isStopped() {
return this.stopped;
}
private void sleep(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void run() {
System.out.println("[Stoppable task] Running");
while (!isStopped()) {
sleep(1000);
System.out.println("...zzzZZZzzz...");
}
System.out.println("[Stoppable task] Finished");
}
}
public static void main(String[] args) {
StoppableTask myTask = new StoppableTask();
Thread thread = new Thread(myTask, "MyTask");
thread.start();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
myTask.stop();
}
}
Output:
[Stoppable task] Running
...zzzZZZzzz...
...zzzZZZzzz...
...zzzZZZzzz...
[Stoppable task] Finished
By default, a thread that is still running when the main thread finishes all its work
keeps the Java VM alive and the process waits for the other thread to terminate.
package dev.bitek.multithreading.example9;
public class ThreadExample9 {
public static void main(String[] args) {
Runnable runnable = () -> {
while (true) {
sleep(1000);
System.out.println("Running");
}
};
Thread thread = new Thread(runnable);
thread.start();
sleep(3000);
System.out.println("Main thread finished work");
}
public static void sleep(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Output:
Running
Running
Main thread finished work
Running
Running
...
...
Running ---> The newly spawned thread keeps the process running even after the main thread finished its work
To prevent this Behavior we need to mark a thread as a daemon before starting it:
Thread thread = new Thread(runnable);
thread.setDaemon(true);
thread.start();
Output:
Running
Running
Main thread finished work
Running ---> Right after main thread finished its work the other thread stops its execution.
Keep in mind that the daemon thread is stopped in an undefined state,
thus the code running inside it can be in the middle of executing some important work
and might leave the work half finished.
Make a thread wait for another thread to finish before stopping its execution.
Main thread has no other work to do after starting the other thread and it finishes immediately:
package dev.bitek.multithreading.example11;
public class ThreadExample11 {
public static void main(String[] args) {
Runnable runnable = () -> {
for (int i = 0; i < 5; i++) {
sleep(1000);
System.out.println("Running");
}
};
Thread thread = new Thread(runnable);
thread.setDaemon(true);
thread.start();
System.out.println("Main thread finished");
}
public static void sleep(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Output:
Main thread finished
To make the main thread wait for the another thread to finish we call the thread.join()
method on the other thread:
package dev.bitek.multithreading.example12;
public class ThreadExample12 {
public static void main(String[] args) {
Runnable runnable = () -> {
for (int i = 0; i < 5; i++) {
sleep(1000);
System.out.println("Running");
}
};
Thread thread = new Thread(runnable);
thread.setDaemon(true);
thread.start();
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Main thread finished");
}
public static void sleep(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Output:
Running
Running
Running
Running
Running
Main thread finished