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!