ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Thread and Runnable
    StaticPL/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

    Diagram of Thread life cycle

    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

    Diagram of spawning threads flow

     

    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

    댓글

Designed by Tistory.