This article has participated in the activity of “New person creation Ceremony”, and started the road of digging gold creation together.

The interview

When I was interviewing for Java development as an interviewer, I asked the following question:

Implement producer and consumer patterns with multiple threads

The standard answer for the interviewer is to implement producers and consumers through queues and here’s the Java pseudocode implementation

ThreadPoolExecutor producter for(int I =0; i<m; I++){Producter worker = new Thread(()->{//TODO: // production data to queue mq.put (data)}); producter.execute(worker); } # create a consumer ThreadPoolExecutor consumer thread pool for(int I =0; i<n; I++){Consumer worker = new Consumer(()->{//TODO: // consume data mq.get ()}); consumer.execute(worker); }Copy the code

It would have ended here, but since the interview was for senior development, I also felt a little low, so I asked again:

Are producers and consumers implementing the Runnable interface? If there is a problem with the system, how do you find out which producer or consumer thread is causing the problem?

After asking this question, the atmosphere began to be a little delicate, and the interviewee began to go too far in answering the question, began to analyze Java memory, or used shared variables to store thread state, etc. Due to this question, the interviewee was finally dismissed by the leader.

Root of the problem

In fact, the candidate answered ok, but he missed the root of the question, which was the Runnable interface.

The Runnable interface is a core artifact of JDK1.0

@FunctionalInterface
public interface Runnable {
    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see     java.lang.Thread#run()
     */
    public abstract void run();
}
Copy the code

It used to be the benchmark for multithreaded programming, and threads were Runnable, but times have changed, and now it’s 2202, and the latest Java is 17. Runnable has no arguments, no returns, and no exceptions. Do you really need Runnable for complex core business?

Callable is a better choice

Back in Java 1.5, Doug Lea created a Callable interface with parameters, returns, and exceptions, which is not as good as Runnable.

/* * @see Executor * @since 1.5 * @author Doug Lea * @param <V> The result type of method {@code call} */ @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; }Copy the code

Take a look at Runnable versus Callable

It can replace the Runnable interface entirely, except that it cannot be executed independently in the Thread class

Future makes you a promise

Multithreaded, that is, asynchronous, where do I get the results?

Well, if you recall the js Promise, this is probably the romance of the programmer. Js gives you a Promise to tell you the result through then when the execution is complete

Doug Lea:

Java gives you a Future and tells you what’s going to happen in the Future.

actions following the corresponding {@code Future.get()} in another thread.

/* * <p>Memory consistency effects: Actions taken by the asynchronous computation * <a href="package-summary.html#MemoryVisibility"> <i>happen-before</i></a> * actions following the corresponding {@code Future.get()} in another thread. * * @see FutureTask * @see Executor * @since 1.5 * @author Doug Lea * @param <V> The result type returned by this Future's {@code  get} method */ public interface Future<V> { */Copy the code

The experiment demonstrates

See how Future notifies you of the results of execution in the future

import java.io.IOException;
import java.util.concurrent.*;

public class FutureImpl {
    static ThreadPoolExecutor pool = new ThreadPoolExecutor(10,10,1, TimeUnit.MINUTES,new ArrayBlockingQueue<>(10));
    public static void main(String[] args) throws IOException {
        Callable<String> goodday = ()->{
            System.out.println("start good day!");
            Thread.sleep(5000);
            return "have a good day!";
        };
        Callable<String> badday = ()->{
            System.out.println("start bad day!");
            Thread.sleep(2000);
            int a = 1;
            int b = 0;
            return "have a bad day!"+(a/b);
        };
        Future<String> future1 = pool.submit(goodday);
        Future<String> future2 = pool.submit(badday);

        pool.execute(()->{
            try {
                System.out.println("future1 "+future1.get());
            } catch (Exception e) {
                System.err.println(e.getMessage());
            }
        });
        pool.execute(()->{
            try {
                System.out.println("future2 "+future2.get());
            } catch (Exception e) {
                System.err.println(e.getMessage());
            }
        });
        System.in.read();
    }
}
Copy the code

The execution result

Let’s have a good day!