Several ways to realize multithreading

This blog series is a record summary of learning concurrent programming. Due to the large number of articles and the scattered time of writing, I arranged a directory post (transmission gate) for easy reference.

Concurrent programming series blog portal

There are many ways to implement multithreaded programming in Java (remember this is a frequently asked interview question, especially when fresh students are looking for jobs).

  • Inherit Thread class and override run method;
  • Implement the Runnable interface and construct the Thread class with the instance of this class as a target
  • Implement the Callable interface;

Inherit Thread class

It's easy to implement multithreaded programming by inheriting the Thread class. In the following code, MyThread class inherits the Thread class and rewrites the run method.

But this method is not very recommended. One of the main reasons is that Java is a single inheritance mode. After the MyThread class inherits the Thread class, it can no longer inherit other classes. So the form of implement ation is better than the form of inheritance. Line and surface will talk about using Runnable interface to realize multithreading.

public class MyThread extends Thread {

    public static final int THREAD_COUNT = 5;

    public static void main(String[] args) {

        List<Thread> threadList = new ArrayList<>();

        for (int i = 0; i < THREAD_COUNT; i++) {
            Thread thread = new MyThread();
            thread.setName("myThread--"+i);
            threadList.add(thread);
        }
        threadList.forEach(var->{var.start();});
    }

    @Override
    public void run() {
        super.run();
        System.out.println("my thread name is:"+Thread.currentThread().getName());
        Random random = new Random();
        int sleepTime = random.nextInt(5);
        try {
            TimeUnit.SECONDS.sleep(sleepTime);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            System.out.println(Thread.currentThread().getName()+" end after "+sleepTime+" seconds");
        }
    }
}

Implementation of Runnable interface and multithreading

Next, we will transform the above code by implementing the Runnable interface.

It can be found that it is also very convenient to realize multithreading programming by implementing Runnable interface. However, there is no need to inherit the Thread class, which reduces the coupling. At the same time, when a Runner object is added, it can be easily shared among threads. Therefore, compared with the way of inheriting Thread, it is more recommended to use Runnable interface to realize multithreading programming.

public class MyThread {

    public static final int THREAD_COUNT = 5;

    public static void main(String[] args) {

        List<Thread> threadList = new ArrayList<>();
        Runner runner = new Runner();

        for (int i = 0; i < THREAD_COUNT; i++) {
            Thread thread = new Thread(runner);
            thread.setName("myThread--"+i);
            threadList.add(thread);
        }
        threadList.forEach(var->{var.start();});
    }


    public static class Runner implements Runnable{
        @Override
        public void run() {
            System.out.println("my thread name is:"+Thread.currentThread().getName());
            Random random = new Random();
            int sleepTime = random.nextInt(5);
            try {
                TimeUnit.SECONDS.sleep(sleepTime);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                System.out.println(Thread.currentThread().getName()+" end after "+sleepTime+" seconds");
            }
        }
    }

}

Implement Callable interface

The above two methods can be easily implemented multithreaded programming. However, there are several obvious defects in these two ways:

  • No return value: if you want to get an execution result, you need to share variables and do more processing.
  • Can't throw exception: can't throw exception declaratively, which increases the complexity of program development in some cases.
  • Unable to cancel the thread manually: you can only wait for the thread to finish executing or meet some end conditions, and cannot cancel the thread task directly.

In order to solve the above problems, in the java.util.concurretn package of JDK5, a new thread implementation mechanism called Callable interface is introduced.

@FunctionalInterface
public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}

After reading the introduction of the Callable interface, the function of this interface is the same as that of the Runnable interface. The main differences between the Callable interface and the Runnable interface are as follows:

  • Callable interface can have return value;
  • The Callable interface can throw exceptions;

The following code can be modified by using the Callable interface:

public class MyThread {

    public static final int THREAD_COUNT = 5;

    public static void main(String[] args) throws Exception {

        ExecutorService executorService = Executors.newFixedThreadPool(THREAD_COUNT);
        Runner runner = new Runner();

        for (int i = 0; i < THREAD_COUNT; i++) {
            Future<Integer> submit = executorService.submit(runner);
            //The get method will block until the thread finishes executing
            System.out.println(submit.get());
        }
        executorService.shutdown();

    }


    public static class Runner implements Callable<Integer> {

        @Override
        public Integer call() throws Exception {
            System.out.println("my thread name is:"+Thread.currentThread().getName());
            Random random = new Random();
            int sleepTime = random.nextInt(500);
            try {
                TimeUnit.SECONDS.sleep(sleepTime);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                System.out.println(Thread.currentThread().getName()+" end after "+sleepTime+" seconds");
            }
            return sleepTime;
        }
    }

}

In the above code, we use the Future class to get the return result. The main methods of Future interface are as follows:

  • isDone(): judge whether the task is completed.
  • isCancelled(): judge whether the task is cancelled.
  • get(): get the calculation result (wait until the result is obtained).
  • cancel(true): cancel the task.
  • get(long,TimeUnit): get the calculation result in the specified time (wait for the result in the long time, and return if you get it; end if you don't get it, and throw a TimeoutException exception).

Tags: Java Programming

Posted on Wed, 04 Dec 2019 14:28:30 -0500 by SBro