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
獲取到了返回值。。。。