本文已参与「新人创作礼」活动,一起开启掘金创作之路。
面试
面试java开发的时候作为面试官,我提了一个问题:
用多线程实现一下生产者和消费者模式
面试者回答的很标准就是通过队列实现生产者和消费者 以下是java伪代码实现
MQ //可以是消息队列也可以是数据库
#创建生产者
ThreadPoolExecutor producter //生产者线程池
for(int i=0;i<m;i++){
Producter worker = new Thread(()->{
//TODO:
// 生产数据到队列
MQ.put(data)
});
producter.execute(worker);
}
#创建消费者
ThreadPoolExecutor consumer //消费者线程池
for(int i=0;i<n;i++){
Consumer worker = new Consumer(()->{
//TODO:
//从队列消费数据
MQ.get()
});
consumer.execute(worker);
}
本来到这里就可以结束了,但是由于面试的是高级开发,我也觉得自己提问有些low于是我又问了一句:
生产者和消费者都是实现了Runnable接口的?如果这个系统出现了问题,你如何排查是哪个生产者线程或者消费者线程发生了问题?
问完这个问题,气氛就开始有些微妙了,面试者回答的就开始扯的有些远,开始分析java内存,或者使用共享变量存储线程状态等 由于这个问题,面试者最后被leader刷掉了。
问题的根本
其实面试者回答的还可以,但是他没看到问题的根本,就是Runnable接口。
Runnable 接口是 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();
}
曾经的它是多线程编程的标杆线程(Thread)的执行入口也都是Runnable的,但是时代在变,现在都2202年了,最新的java都到17了。 Runnable它无参数,无返回,无异常,这样的三无产品在做复杂的核心业务时你真的还要用么?
Callable更好的选择
早在 Java 1.5 这个神奇的版本中 大神Doug Lea 就创造了Callable这个接口,有参数,有返回,还有异常,这不比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;
}
看一下Runnable 和Callable的对比图
除了不能在Thread类中单独执行,它完全可以取代Runnable接口
Future给你一个承诺
多线程,也就是异步的,我从哪里获得执行结果呢?
嗯,回想一下js 的Promise,这个大概就是程序员的浪漫吧,js中时个你一个Promise保证,保证在执行完成时通过then告诉你结果
还是大神Doug Lea :
java则是给你一个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> {
*/
实验演示
看看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();
}
}
执行结果
今天就到这里吧,have a good day!