Realization of correct posture of multi thread based on Concurrency

Keywords: Java Lambda Oracle

How many ways to implement multithreading?

For the implementation of JAVA multithreading, both online and all kinds of books have the same view, some say two, some say four, we can see many kinds of answers in a glance at Baidu

How many are they? Let's look up the JAVA API document: https://docs.oracle.com/javase/9/docs/api/java/lang/Thread.html

From the documents, we can clearly see that there are two ways to implement multithreading: one is to declare a class as a subclass of Thread, and the other is to declare a class that implements the Runnable interface.

Comparison of two implementation methods

To implement the runnable interface:

/**
 * @author Chen
 * @Description Using runnable interface to realize multithreading
 * @create 2019-11-04 21:38
 */
public class RunnableStyle implements Runnable{
    public static void main(String[] args) {
        Thread thread = new Thread(new RunnableStyle());
        thread.start();
    }

    @Override
    public void run() {
        System.out.println("Use runnable Interface implementation multithreading");
    }
}

Inherit Thread class:

/**
 * @author Chen
 * @Description Using Thread mode to realize multithreading
 * @create 2019-11-04 21:41
 */
public class ThreadStyle extends Thread{
    public static void main(String[] args) {
        Thread thread = new ThreadStyle();
        thread.start();
    }

    @Override
    public void run() {
        System.out.println("Use Thread Class to implement multithreading");
    }
}

Use Runable or Thread?

Since multithreading can be implemented, should we actually use the option to create it or just do it?

The answer is that it's better to implement Runable.

The main reasons are as follows:

1. From the perspective of decoupling, multithreaded tasks (that is, the content of the run method) should be decoupled from the Thread class,

Not all together.

2. If we use the inherited Thread class to implement multithreading, we can only create a new independent Thread for each task we want to create, and this kind of loss is relatively large, so we need to create, destroy and so on. With Runnable interface, we can use Thread pool later, which can reduce the loss of new threads.

3. Because JAVA is a single inheritance, once the Thread is inherited, it cannot inherit other classes, which limits the extensibility of the program.

What's the difference between running and Thread creation?

Using the method of Runable to create, we use a Thread class and a construction method with parameters. Using the Thread method to create, we use a construction method without parameters of the Thread class. Both of these methods override the Run method. Next, let's see how the Run method in the Thread class is implemented:

    /* What will be run. */
    private Runnable target;

    /**
     * If this thread was constructed using a separate
     * <code>Runnable</code> run object, then that
     * <code>Runnable</code> object's <code>run</code> method is called;
     * otherwise, this method does nothing and returns.
     * <p>
     * Subclasses of <code>Thread</code> should override this method.
     *
     * @see     #start()
     * @see     #stop()
     * @see     #Thread(ThreadGroup, Runnable, String)
     */
    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }

The above is the source code of a part of the Thread class. We can see that when executing the run method, we will first determine whether to pass in the implementation class of the Runable interface. If it is passed in, we will execute the run method in the implementation class of the interface. If it is not passed in, we will directly execute the run method rewritten by the subclass. Therefore, in essence, the two methods execute the run method, but only the run method The sources of law are different.

Wrong viewpoint

Thread pool is also a way to create a new thread

/**
 * @author Chen
 * @Description Use thread pool to create threads
 * @create 2019-11-05 9:40
 */
public class ThreadPoolStyle {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i <1000 ; i++) {
            executorService.submit(new Task());
        }
    }

    static class  Task implements Runnable{
        @Override
        public void run() {
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName());
        }
    }
}

Let's take a look at how threads are created inside excutors:

You can see that the new thread in the following code is created by passing in a Runnable and then creating it in a new thread, so although the appearance of the creation is not the same, the principle is to realize multithreading through the Runnable interface.

    /**
     * The default thread factory
     */
    static class DefaultThreadFactory implements ThreadFactory {
        private static final AtomicInteger poolNumber = new AtomicInteger(1);
        private final ThreadGroup group;
        private final AtomicInteger threadNumber = new AtomicInteger(1);
        private final String namePrefix;

        DefaultThreadFactory() {
            SecurityManager s = System.getSecurityManager();
            group = (s != null) ? s.getThreadGroup() :
                                  Thread.currentThread().getThreadGroup();
            namePrefix = "pool-" +
                          poolNumber.getAndIncrement() +
                         "-thread-";
        }

        public Thread newThread(Runnable r) {
            Thread t = new Thread(group, r,
                                  namePrefix + threadNumber.getAndIncrement(),
                                  0);
            if (t.isDaemon())
                t.setDaemon(false);
            if (t.getPriority() != Thread.NORM_PRIORITY)
                t.setPriority(Thread.NORM_PRIORITY);
            return t;
        }
    }

Creating threads through Callable and FutureTask is also a way to create new threads

Through Callable and Future:

/**
 * @author Chen
 * @Description Using Callable, Future, and FutureTask
 * @create 2019-11-05 10:03
 */
public class FutureTaskStyle {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService executorService = Executors.newCachedThreadPool();
        Future<String> future = executorService.submit(new Callable<String>() {
            @Override
            public String call() throws Exception {
                Thread.sleep(500);
                return "future result";
            }
        });
        System.out.println(System.currentTimeMillis());
        System.out.println(future.get());
        System.out.println(System.currentTimeMillis());
    }
}

Through Callable and FutureTask:

/**
 * @author Chen
 * @Description Using Callable, Future, and FutureTask
 * @create 2019-11-05 10:03
 */
public class FutureTaskStyle {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService executorService = Executors.newCachedThreadPool();
        FutureTask<String> futureTask = new FutureTask<String>(new Callable<String>() {
            @Override
            public String call() throws Exception {
                Thread.sleep(500);
                return "future result";
            }
        });
        new Thread(futureTask).start();
        System.out.println(System.currentTimeMillis());
        System.out.println(futureTask.get());
        System.out.println(System.currentTimeMillis());
    }
}

Use ctrl+alt+u to view the inheritance diagram of FutureTask: you can see that RunableFuture inherits the Future and Runnable interfaces, while FutureTask implements RunableFuture, so FutureTask also indirectly implements the Runable interface. So the principle of this method is the same as that of using Runnable.

By timer

/**
 * @author Chen
 * @Description Use timer to create new thread
 * @create 2019-11-05 10:26
 */
public class TimmerStyle {
    public static void main(String[] args) {
        Timer timer = new Timer();
        timer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName());
            }
        },1000,1000);

    }
}

Let's see that TimerTask also implements the Runnable interface, which is essentially created by using the Runnable interface.

By anonymous inner class or Lambda expression

/**
 * @author Chen
 * @Description Multithreading using anonymous inner class and lamada expression
 * @create 2019-11-05 10:36
 */
public class AnonymousInnerClassStyle {
    public static void main(String[] args) {
        //Anonymous Inner Class 
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName());
            }
        }).start();

        //Lambda expression
        new Thread(()-> System.out.println(Thread.currentThread().getName())).start();

    }
}

In fact, the effect of anonymous inner class and Lambda expression implementation is the same, only a new writing method is changed.

summary

How many ways to implement multithreading? From different perspectives, there will be different answers. If we look at the nature of multithreading, there are generally two kinds: one is to implement the Runnable interface, the other is to inherit the Thread class; and from the code implementation level, there will be many kinds, such as Thread pool, FutureTask anonymous inner class, Lambda expression, etc.

In fact, in essence, the two implementation methods are the same. They call the run method of the Thread class, and there is a judgment in the run method that if the incoming runnable is not empty, the code in the method of the run method itself will be executed.

In general, we will create a new thread by using the Runable interface, mainly for the following reasons:

From the perspective of program coupling, the task code to be submitted is separated from the code in Thread class to realize low coupling;

From the perspective of extensibility, because JAVA is single inheritance, the way of inheriting Thead class cannot inherit other base classes, which reduces the class extensibility of the program;

From the perspective of resource consumption, we can use thread pool to manage threads by using Runnable, which can reduce the resource loss caused by new threads destroying threads.

Posted by corkg on Mon, 04 Nov 2019 19:51:27 -0800