-
Thread and RunnableStaticPL/JAVA 2019. 8. 18. 20:33
1. Overview
Summarize Thread in JAVA. Thread class provides constructors and methods to create and perform operations on a thread. It is derived from Object class and implements the Runnable interface.
Threads can be created by using two mechanisms :
- Extending the Thread class
- Implementing the Runnable Interface
2. The life cycle of Threads
2.1 NEW
- Thread instance newly created, have not started yet
2.2 RUNNABLE
- running or waiting for resource allocation for running
2.3 BLOCKED
- waiting to acquire a monitor lock to enter or re-enter a synchronized block
2.4 WAITING
- waiting for some other thread to perform a particular action without any time limit
2.5 TIME_WAITING
- WAITING with timeout
2.6 TERMINATED
- Has completed or was terminated abnormally
3. A typical workflow of Threads in the JAVA Apps
For each program, The Main thread is created by JVM. that thread verifies the existence of the main() method, and then initialize the class.
- Other child threads are spawned from the Main thread.
- Mostly, the Main thread will be finished because it involves various shutdown actions
4. Thread
Creating a new thread simply by extending a class from Thread and overriding it's run() method. The run() method contains the code that is executed inside the created Thread.
public class ThreadExample extends Thread { // run() method contains the code that is executed by the thread. @Override public void run() { System.out.println("Inside : " + Thread.currentThread().getName()); } public static void main(String[] args) { System.out.println("Inside : " + Thread.currentThread().getName()); System.out.println("Creating thread..."); Thread thread = new ThreadExample(); // creating a thread with a custom name using Thread(String name) instread. System.out.println("Starting thread..."); thread.start(); } }
4.1 Difference run() and start() in Thread
So what is the difference between the start and run method? The main difference is that when program calls start() method a new Thread is created and code inside run() method is executed in new Thread while if you call run() method directly no new Thread is created and code inside run() will execute on current Thread. Most of the time calling run() is bug or programming mistake because a caller has the intention of calling start() to create a new thread and this error can be detected by many static code coverage tools like findbugs. If you want to perform time-consuming task than always call start() method otherwise your main thread will be stuck while performing the time-consuming task if you call run() method directly. Another difference between start vs run in Java thread is that you can not call start() method twice on thread object. once started, the second call of start() will throw IllegalStateException in Java while you can call run() method twice.
public class StartVsRunCall{ public static void main(String args[]) { //creating two threads for start and run method call Thread startThread = new Thread(new Task("start")); Thread runThread = new Thread(new Task("run")); startThread.start(); //calling start method of Thread - will execute in new Thread runThread.run(); //calling run method of Thread - will execute in current Thread } /* * Simple Runnable implementation */ private static class Task implements Runnable{ private String caller; public Task(String caller){ this.caller = caller; } @Override public void run() { System.out.println("Caller: "+ caller + " and code on this Thread is executed by : " + Thread.currentThread().getName()); } } } Output: Caller: start and code on this Thread is executed by : Thread-0 Caller: run and code on this Thread is executed by : main Read more: https://javarevisited.blogspot.com/2012/03/difference-between-start-and-run-method.html#ixzz6EtKFWI3E
4.2 Create Thread
4.2.1 Thread Inheritance
public class demo { public static void main(String[] args) { // inline anonymous implementation of Runnable inheritance Thread t1 = new Thread(new Runnable() { @Override public void run() { System.out.println("Hello from " + Thread.currentThread().getName()); } }); // Create concrete Class which is inherit Thread Thread t2 = new NewThread(); t1.start(); t2.start(); } // Inherit Thread private static class NewThread extends Thread { @Override public void run() { System.out.println("Hello from " + Thread.currentThread().getName()); System.out.println("Hello from " + this.getName()); } } }
4.2.2 Example
public class CaseDemo { public static final int MAX_PASSWORD = 9999; public static void main(String[] args) { Random random = new Random(); Vault vault = new Vault(random.nextInt(MAX_PASSWORD)); List<Thread> threads = new ArrayList<>(); // polymorphism threads.add(new AscendingHakcerThread(vault)); threads.add(new DescendingHackerThread(vault)); threads.add(new PoliceThread()); for (Thread thread : threads) { thread.start(); } } private static class Vault { private int password; public Vault(int password) { this.password = password; } public boolean isCorrectPassword(int guess) { try { Thread.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } return this.password == guess; } } private static abstract class HackerThread extends Thread { protected Vault vault; public HackerThread(Vault vault) { this.vault = vault; this.setName(this.getClass().getSimpleName()); this.setPriority(Thread.MAX_PRIORITY); } @Override public synchronized void start() { System.out.println("Starting thread " + this.getName()); super.start(); } } private static class AscendingHakcerThread extends HackerThread { public AscendingHakcerThread(Vault vault) { super(vault); } @Override public void run() { for (int guess = 0; guess < MAX_PASSWORD; guess++) { if(vault.isCorrectPassword(guess)) { System.out.println(this.getName() + " guessed the password " + guess); System.exit(0); } } } } private static class DescendingHackerThread extends HackerThread { public DescendingHackerThread(Vault vault) { super(vault); } @Override public void run() { for (int guess = MAX_PASSWORD; guess >= 0 ; guess--) { if(vault.isCorrectPassword(guess)) { System.out.println(this.getName() + " guessed the password " + guess); System.exit(0); } } } } private static class PoliceThread extends Thread { @Override public void run() { for (int i = 10; i > 0 ; i--) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(i); } System.out.println("Game over for you hackers"); System.exit(0); } } }
4.3 Terminate Thread
4.3.1 Thread.interrupt()
The application will not stop as long as at least one thread is still running even if main thread is terminated.
public class demo { public static void main(String[] args) { Thread thread = new Thread(new LongComputationTask(new BigInteger("200000"), new BigInteger("1000000000"))); thread.start(); // revoking interrupt thread.interrupt(); } private static class LongComputationTask implements Runnable { private BigInteger base; private BigInteger power; public LongComputationTask(BigInteger base, BigInteger power) { this.base = base; this.power = power; } @Override public void run() { System.out.println(base+"^"+power+" = " + pow(base,power)); } private BigInteger pow(BigInteger base, BigInteger power) { BigInteger result = BigInteger.ONE; for (BigInteger i = BigInteger.ZERO; i.compareTo(power) != 0; i = i.add(BigInteger.ONE)) { // Interrupt handling is necessary if(Thread.currentThread().isInterrupted()) { System.out.println("Prematurely interrupted computation"); return BigInteger.ZERO; } result = result.multiply(base); } return result; } } }
4.3.2 Daemon Thread
Daemon thread is background threads that do not prevent an application from exiting if the main thread terminates.
- Example: File saving thread in a Text Editor
Code in a worker thread is not under our control, and we do not want it to block our application from terminating.
- Example: Worker thread that uses an external library
public class demo { public static void main(String[] args) { Thread thread = new Thread(new LongComputationTask(new BigInteger("200000"), new BigInteger("1000000000"))); thread.setDaemon(true); thread.start(); thread.interrupt(); } private static class LongComputationTask implements Runnable { private BigInteger base; private BigInteger power; public LongComputationTask(BigInteger base, BigInteger power) { this.base = base; this.power = power; } @Override public void run() { System.out.println(base+"^"+power+" = " + pow(base,power)); } private BigInteger pow(BigInteger base, BigInteger power) { BigInteger result = BigInteger.ONE; for (BigInteger i = BigInteger.ZERO; i.compareTo(power) != 0; i = i.add(BigInteger.ONE)) { // No need to handle interrupt if thread is daemon // if(Thread.currentThread().isInterrupted()) { // System.out.println("Prematurely interrupted computation"); // return BigInteger.ZERO; // } result = result.multiply(base); } return result; } } }
4.4 Thread coordination - Dependency
What if one thread depends on another thread?
4.4.1 Consideration
- Do not rely on the order of execution
- Always use thread coordination
- Design code for a worst-case scenario
- Threads may take an unreasonably long time. Always use the Thread.join(..) with a time limit.
4.4.2 Thread.join()
- More control over independent threads
- Safely collect and aggregate results
- Gracefully handle runaway threads using Thread.join(timeout)
public class Demo { public static void main(String[] args) throws InterruptedException { List<Long> inputNumbers = Arrays.asList(1000000000L, 3435L, 35435L, 2324L, 4656L, 23L, 2435L, 5566L); List<FactorialThread> threads = new ArrayList<>(); for (long inputNumber: inputNumbers) { threads.add(new FactorialThread(inputNumber)); } for(Thread thread: threads) { // enable subthreads is terminated if Main thread is finished thread.setDaemon(true); thread.start(); } for (Thread thread: threads) { // time limit for waiting to join thread.join(2000); } for (int i = 0; i < inputNumbers.size(); i++) { FactorialThread factorialThread = threads.get(i); if(factorialThread.isFinished()) { System.out.println("Factorial of " + inputNumbers.get(i) + " is " + factorialThread.getResult()); } else { System.out.println("The calculation for " + inputNumbers.get(i) + " is still in progress"); } } } public static class FactorialThread extends Thread { private long inputNumber; private BigInteger result = BigInteger.ZERO; private boolean isFinished = false; public FactorialThread(long inputNumber) { this.inputNumber = inputNumber; } @Override public void run() { this.result = factorial(inputNumber); this.isFinished = true; } public BigInteger factorial(long n) { BigInteger tempResult = BigInteger.ONE; for (long i = n; i > 0 ; i--) { tempResult = tempResult.multiply(new BigInteger(Long.toString(i))); } return tempResult; } public BigInteger getResult() { return result; } public boolean isFinished() { return isFinished; } } }
5. Runnable
Runnable interface is the primary template for any object that is intended to be executed by a thread. it defines a single method run(), which is meant to contain the code that is executed by the thread. Any class whose instance needs to be executed by a thread should implement the Runnable interface.
Thread class itself implements Runnable with an empty implementation of run() method.
Runnable are basically containers for code. They just contain some code that should be executed but runnable does not specify how this code will be executed and on which thread.
for creating a new thread, create an instance of the class that implements Runnable interface and then pass that instance to Thread(Runnable target) constructor.
public class RunnableExample implements Runnable { public static void main(String[] args) { System.out.println("Inside : " + Thread.currentThread().getName()); System.out.println("Creating Runnable..."); Runnable runnable = new RunnableExample(); System.out.println("Creating Thread..."); Thread thread = new Thread(runnable); System.out.println("Starting Thread..."); thread.start(); } @Override public void run() { System.out.println("Inside : " + Thread.currentThread().getName()); } }
Note that, instead of creating a class which implements Runnable and then instantiating that class to get the runnable object, you can create an anonymous runnable by using Java's anonymous class syntax. this approach makes code looking more concise.
public class RunnableExampleAnonymousClass { public static void main(String[] args) { System.out.println("Inside : " + Thread.currentThread().getName()); System.out.println("Creating Runnable..."); Runnable runnable = new Runnable() { @Override public void run() { System.out.println("Inside : " + Thread.currentThread().getName()); } }; System.out.println("Creating Thread..."); Thread thread = new Thread(runnable); System.out.println("Starting Thread..."); thread.start(); } }
Also, lambda expression looks like this.
public class RunnableExampleLambdaExpression { public static void main(String[] args) { System.out.println("Inside : " + Thread.currentThread().getName()); System.out.println("Creating Runnable..."); Runnable runnable = () -> { System.out.println("Inside : " + Thread.currentThread().getName()); }; System.out.println("Creating Thread..."); Thread thread = new Thread(runnable); System.out.println("Starting Thread..."); thread.start(); } }
5.1 Pitfalls
- Hard to manage resources, context switching, memory usage
- Immature of the consistent creation of new threads
- Immature of the number of a concurrent live thread
- Immature of deallocating threads process for avoiding memory leaks
- Not suitable for production-ready
6. Thread vs Runnable
by Joshua Bloch in his book Effective Java as Item 18 that says favor composition over inheritance. So I try to use composition as much as I can instead of inheritance.
7. Start Thread Execution Example
7. References
https://www.geeksforgeeks.org/multithreading-in-java/
https://www.baeldung.com/java-thread-lifecycle
https://www.baeldung.com/java-start-thread
https://www.callicoder.com/java-multithreading-thread-and-runnable-tutorial/
https://javarevisited.blogspot.com/2012/03/difference-between-start-and-run-method.html
'StaticPL > JAVA' 카테고리의 다른 글
CompletableFuture, and Future (0) 2019.08.20 Threadlocal (0) 2019.08.19 Stream (0) 2019.08.18 Difference between Abstract Class and Interface (0) 2019.08.18 JAVA Collection Framework (0) 2019.08.18 - Extending the Thread class