1,假如你们公司项目现在有这样需求,需求图如下:
1) 需求:
1,获取用户信息;
2,获取用户余额信息;
3,获取用户积分信息;
4,获取用户未读消息总数;
5,获取用户未支付订单数;
2) 遇见这样的需求大多数开发者首先想到的是可以采用连表查询出这些数据。没错,在单体环境中确实没有问题,可以折磨做。但是在分布式集群环境中就行不通啦,因为这些数据都在不同的数据库或服务中。我们都会把这些模块做成一个一个的服务,用于http远程调用。一般开发人员的写法如下:
service服务:
public class ServiceAll {
/**
* 模拟用户服务
* @return
* @throws InterruptedException
*/
public static String getAccountService() throws InterruptedException {
System.out.println("获取用户信息。。。");
//模拟远程调用耗时
Thread.sleep(1000);
String acountName= "张三";
return acountName;
}
/**
* 模拟余额服务
* @return
* @throws InterruptedException
*/
public static Integer getBalanceService() throws InterruptedException {
System.out.println("获取余额信息。。。");
//模拟远程调用耗时
Thread.sleep(1000);
Integer balance = 100;
return balance;
}
/**
* 模拟积分服务
* @return
* @throws InterruptedException
*/
public static Integer getIntegralService() throws InterruptedException {
System.out.println("获取积分信息。。。");
//模拟远程调用耗时
Thread.sleep(1000);
Integer integral = 50;
return integral;
}
/**
* 模拟消息服务
* @return
* @throws InterruptedException
*/
public static String getMessageService() throws InterruptedException {
System.out.println("获取消息信息。。。");
//模拟远程调用耗时
Thread.sleep(1000);
String message = "我是消息";
return message;
}
/**
* 模拟订单服务
* @return
* @throws InterruptedException
*/
public static String getOrderService() throws InterruptedException {
System.out.println("获取订单信息。。。");
//模拟远程调用耗时
Thread.sleep(1000);
String order = "订单xx1";
return order;
}
}
service层:
public class ServiceImpl {
/**
* 模拟service
* @throws InterruptedException
*/
public static Map<String,Object> getService() throws InterruptedException{
String accountName = ServiceAll.getAccountService();
Integer balance = ServiceAll.getBalanceService();
Integer integral = ServiceAll.getIntegralService();
String message = ServiceAll.getMessageService();
String order = ServiceAll.getOrderService();
Map<String,Object> map = new HashMap<String,Object>();
map.put("accountName", accountName);
map.put("balance", balance);
map.put("integral", integral);
map.put("message", message);
map.put("order", order);
return map;
}
}
controller层:
public class Controller {
/**
* 模拟controller
* @param args
* @throws InterruptedException
* @throws ExecutionException
*/
public static void main(String[] args) throws InterruptedException, ExecutionException {
long start = System.currentTimeMillis();
Map<String,Object> map = ServiceImpl.getService();
System.out.println("map="+map);
long end = System.currentTimeMillis();
System.out.println("耗时:"+(end-start));
}
}
运行结果:
获取用户信息。。。
获取余额信息。。。
获取积分信息。。。
获取消息信息。。。
获取订单信息。。。
map={balance=100, accountName=张三, integral=50, message=我是消息, order=订单xx1}
耗时:5002
3,很明显,效率很低,而且如果业务增加,耗时会更高;这是为什么呢?
主要是业务按串行执行的(顺序执行),如果按异步执行就好了,下面给大家介绍一个Callable接口,Callable和Runable类似,但又有不同:1,Callable有返回值,Runable没有返回值,2,Callable可以抛出异常,Runable不能抛出异常。
4,使用Callable解决上述问题:
service层:
public class ServiceImpl {
/**
* 模拟service
* @throws InterruptedException
*/
public static Map<String,Object> getService() throws InterruptedException, ExecutionException {
Callable<String> task1= new Callable<String>() {
@Override
public String call() throws Exception {
String accountName = ServiceAll.getAccountService();
return accountName;
}
};
Callable<Integer> task2= new Callable<Integer>() {
@Override
public Integer call() throws Exception {
Integer balance = ServiceAll.getBalanceService();
return balance;
}
};
Callable<Integer> task3= new Callable<Integer>() {
@Override
public Integer call() throws Exception {
Integer integral = ServiceAll.getIntegralService();
return integral;
}
};
Callable<String> task4= new Callable<String>() {
@Override
public String call() throws Exception {
String message = ServiceAll.getMessageService();
return message;
}
};
Callable<String> task5= new Callable<String>() {
@Override
public String call() throws Exception {
String order = ServiceAll.getOrderService();
return order;
}
};
FutureTask<String> futureTask1 = new FutureTask<>(task1);
new Thread(futureTask1).start();
FutureTask<Integer> futureTask2 = new FutureTask<>(task2);
new Thread(futureTask2).start();
FutureTask<Integer> futureTask3 = new FutureTask<>(task3);
new Thread(futureTask3).start();
FutureTask<String> futureTask4 = new FutureTask<>(task4);
new Thread(futureTask4).start();
FutureTask<String> futureTask5 = new FutureTask<>(task5);
new Thread(futureTask5).start();
String accountName = futureTask1.get();
Integer balance = futureTask2.get();
Integer integral = futureTask3.get();
String message = futureTask4.get();
String order = futureTask5.get();
Map<String,Object> map = new HashMap<String,Object>();
map.put("accountName", accountName);
map.put("balance", balance);
map.put("integral", integral);
map.put("message", message);
map.put("order", order);
return map;
}
}
controller层:
public class Controller {
/**
* 模拟controller
* @param args
* @throws InterruptedException
* @throws ExecutionException
*/
public static void main(String[] args) throws InterruptedException, ExecutionException {
long start = System.currentTimeMillis();
Map<String,Object> map = ServiceImpl.getService();
System.out.println("map="+map);
long end = System.currentTimeMillis();
System.out.println("耗时:"+(end-start));
}
}
运行结果:
获取消息信息。。。
获取用户信息。。。
获取订单信息。。。
获取余额信息。。。
获取积分信息。。。
map={balance=100, accountName=张三, integral=50, message=我是消息, order=订单xx1}
耗时:1002
可以看出,使用了Callable后效率提升了4倍。
下面讲讲FutureTask为什么有返回值?
5,模拟实现FutureTask:
使用效果:
Callable<String> task1= new Callable<String>() {
@Override
public String call() throws Exception {
String accountName = ServiceAll.getAccountService();
return accountName;
}
};
FutureTask<String> futureTask1 = new FutureTask<>(task1);
new Thread(futureTask1).start();
String accountName = futureTask1.get();
1, 由源码可知FutureTask实现了RunnableFuture接口,而RunnableFuture又继承Runnable, Future接口。
public class FutureTask<V> implements RunnableFuture<V> {
//。。。。。。。。。。。。。。。
}
public interface RunnableFuture<V> extends Runnable, Future<V> {
/**
* Sets this Future to the result of its computation
* unless it has been cancelled.
*/
void run();
}
所以我们自定义的ReycoFutureTask也实现Runnable, Future接口。
public class ReycoFutureTask<V> implements Runnable,Future<V>{
public V get() throws InterruptedException, ExecutionException {
return null;
}
@Override
public void run() {
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
return false;
}
@Override
public boolean isCancelled() {
return false;
}
@Override
public boolean isDone() {
return false;
}
@Override
public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
return null;
}
}
2,由以下代码可以看出需要有一个参数为Callable的构造器。
FutureTask<String> futureTask1 = new FutureTask<>(task1);
实现构造器:
public class ReycoFutureTask<V> implements Runnable,Future<V>{
final Callable<V> callable;
public ReycoFutureTask(Callable<V> callable) {
super();
this.callable = callable;
}
public V get() throws InterruptedException, ExecutionException {
return null;
}
@Override
public void run() {
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
return false;
}
@Override
public boolean isCancelled() {
return false;
}
@Override
public boolean isDone() {
return false;
}
@Override
public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
return null;
}
}
3, 下面这段代码大家很熟悉,就是调用Runable的run的;
new Thread(futureTask5).start();
所以我们需要实现run方法;
下面这段代码有返回值,所以还要实现get方法
String accountName = futureTask1.get();
由Callable可知,调用Callable的call方法可以返回值,那改怎么写了?我们知道run方法是通过线程执行的,所以call方法在run方法中调用,而ge方法想要获取到返回值必须通过属性获取,所以我们定义一个属性结果值:result;并在run方法中调用call方法的返回值赋值与result;代码如下:
public class ReycoFutureTask<V> implements Runnable,Future<V>{
final Callable<V> callable;
V result;
public ReycoFutureTask(Callable<V> callable) {
super();
this.callable = callable;
}
public V get() throws InterruptedException, ExecutionException {
if(null != result){
return result;
}
return result;
}
@Override
public void run() {
try {
result = callable.call();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
return false;
}
@Override
public boolean isCancelled() {
return false;
}
@Override
public boolean isDone() {
return false;
}
@Override
public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
return null;
}
}
运行中我们发现结果值不对,为null,这是因为get方法直接获取值,而这个时候run方法还没执行,所以获取不到反正值,
那改怎么办了? get方法阻塞,那又有问题了什么时候唤醒阻塞呢?很明显call方法执行完后可以唤醒阻塞。代码如下:
public class ReycoFutureTask<V> implements Runnable,Future<V>{
final Callable<V> callable;
V result;
public ReycoFutureTask(Callable<V> callable) {
super();
this.callable = callable;
}
public V get() throws InterruptedException, ExecutionException {
synchronized (this) {
this.wait();
}
return result;
}
@Override
public void run() {
try {
result = callable.call();
synchronized (this) {
this.notifyAll();
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
return false;
}
@Override
public boolean isCancelled() {
return false;
}
@Override
public boolean isDone() {
return false;
}
@Override
public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
return null;
}
}
到此模拟FutureTask算结束了,下面我们试试效果:
public class Test {
public static void main(String[] args) throws InterruptedException, ExecutionException {
ReycoFutureTask<Integer> task = new ReycoFutureTask<Integer>(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
System.out.println(Thread.currentThread().getName());
Integer a = 1+1;
return a;
}
});
new Thread(task).start();
Integer a = task.get();
System.out.println(a);
}
}
执行结果:
Thread-0
2
获取到了返回值。。。。