java8使用Lambda表達式編寫并發
- 1.提倡使用非阻塞性IO
- 2.回調
- 3.Lambda化
- 4.末日金字塔
- 5.Future
- 6.CompletableFuture
- 7.supplyAsync
- 8.CompletableFuture一些常用方法
- 9.響應式程式設計
- 10.總結
1.提倡使用非阻塞性IO
阻塞性IO對于大量的資料的支援性并不是很好,如果有大量的IO操作等待執行,那麼使用阻塞性IO就會降低整個程式的性能。
相反,使用非阻塞性IO就可以節省大量的時間。
通過名字就能明白,阻塞性IO就是在進行IO操作時,程式無法做其他的事情,隻能等待IO完成後,在繼續進行程式。
非阻塞行IO就是在進行IO操作的同時,軟體可以做其他的事情。
2.回調
package intf;
public interface CallBack {
default void callBack(){
System.out.println("callBack default call back");
}
}
package intf;
public interface Student {
default void answer(CallBack callBack){
System.out.println("student default answer");
callBack.callBack();
}
}
package intf;
public interface Teacher extends CallBack{
default void question(Student student){
System.out.println("teacher default question");
student.answer(this);
}
}
package main;
import intf.CallBack;
import intf.Student;
import intf.Teacher;
import org.junit.Test;
public class Main {
@Test
public void test(){
new Teacher() {
@Override
public void question(Student student){
Teacher.super.question(student);
System.out.println("inner teacher");
}
@Override
public void callBack(){
Teacher.super.callBack();
System.out.println("inner callBack");
}
}.question(new Student() {
@Override
public void answer(CallBack callBack){
Student.super.answer(callBack);
System.out.println("inner student");
}
});
}
}
teacher default question
student default answer
callBack default
3.Lambda化
package intf;
@FunctionalInterface
public interface CallBack {
// default void callBack(){
// System.out.println("callBack default call back");
// }
void callBack();
}
package intf;
@FunctionalInterface
public interface Student {
// default void answer(CallBack callBack){
// System.out.println("student default answer");
// callBack.callBack();
// }
void answer(CallBack callBack);
}
package intf;
@FunctionalInterface
public interface Teacher{
// default void question(Student student){
// System.out.println("teacher default question");
// student.answer(this);
// }
void question(Student student);
}
@Test
public void test(){
CallBack callBack = () -> System.out.println("callBack");
Teacher Wangteacher = (student) -> {
System.out.println("Wang teacher");
student.answer(callBack);
};
Student zhangStudent = (cal) -> {
System.out.println("Zhang student");
cal.callBack();
};
Wangteacher.question(zhangStudent);
}
Wang teacher
Zhang student
callBack
4.末日金字塔
可以看到未Lambda化的代碼現在隻是兩層,如果有多層,那麼随着縮進,代碼都被擠到螢幕的右側。
這種随着調用關系的增加,代碼有規律的右移現象被稱為末日金字塔(不知道是誰起的這個名字)
末日金字塔是良好代碼風格的一個反例,在if語句,匿名内部類等情況下比較多見,特别是if語句中,如果if的層數大于2層,很容易發生末日金字塔的情況。
是以開發時盡可能的避免出現末日金字塔問題。
如果現在的代碼已經出現末日金字塔的問題,那麼盡快進行重構是一個非常明智的選擇。
5.Future
Future像一張欠條,方法傳回不是一個值,而是傳回一個Future對象。
方法一開始執行的時候,傳回一個欠條,等待一段時間後,可以使用欠條換錢。
需要注意:
Future對象的get方法會阻塞目前線程進行阻塞性擷取值。
源碼
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
使用Future容易因為調用get方法造成阻塞
6.CompletableFuture
CompletableFuture結合了回調和Future的思想:
使用CompletableFuture與使用Stream類似,擁有許多的方法可以調用。
CompletableFuture最常用的情景之一是異步執行一段代碼,然後傳回執行結果。
7.supplyAsync
有一個工廠方法,用來建立異步的CompletableFuture執行個體。
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) {
return asyncSupplyStage(asyncPool, supplier);
}
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,
Executor executor) {
return asyncSupplyStage(screenExecutor(executor), supplier);
}
static <U> CompletableFuture<U> asyncSupplyStage(Executor e,
Supplier<U> f) {
if (f == null) throw new NullPointerException();
CompletableFuture<U> d = new CompletableFuture<U>();
e.execute(new AsyncSupply<U>(d, f));
return d;
}
@SuppressWarnings("serial")
static final class AsyncSupply<T> extends ForkJoinTask<Void>
implements Runnable, AsynchronousCompletionTask {
CompletableFuture<T> dep; Supplier<T> fn;
AsyncSupply(CompletableFuture<T> dep, Supplier<T> fn) {
this.dep = dep; this.fn = fn;
}
public final Void getRawResult() { return null; }
public final void setRawResult(Void v) {}
public final boolean exec() { run(); return true; }
public void run() {
CompletableFuture<T> d; Supplier<T> f;
if ((d = dep) != null && (f = fn) != null) {
dep = null; fn = null;
if (d.result == null) {
try {
d.completeValue(f.get());
} catch (Throwable ex) {
d.completeThrowable(ex);
}
}
d.postComplete();
}
}
}
很明顯的看出這個工廠方法執行的操作:
1.傳回一個CompletableFuture執行個體
2.使用傳入的Executor執行
3.使用内部類定義的方式進行許多費時的操作,但是因為使用了CompletableFuture的執行個體,是以初始化的線程與目前線程不是同一個線程。
需要注意的問題:
CompletableFuture執行個體并不是保證完成的。線上程執行的過程中發生異常,可能會導緻線程異常,最後得不到傳回值。
那麼這個欠條就是壞賬了。。
8.CompletableFuture一些常用方法
- 在鍊的末端執行一些代碼不傳回任何值:thenAccepy,thenRun
- 轉換CompletableFuture的值,類似Stream的map方法:thenApply
- 當CompletableFuture出現異常時,可以使用exceptionally方法恢複,接受函數,傳回替代值
- 存在一個map,既包含正常情況的結果,又包括異常情況的結果:handle
- 當CompletableFuture存在異常時,需要調試到底是什麼問題:isDone,isCompleted,Exceptionally
9.響應式程式設計
RxJava類庫
響應式程式設計:發膜護發的傳回值從單一的傳回值推廣到資料流。
響應式程式設計實際是一種聲明式程式設計。
舉個例子:
在excel表格中,在某個單元格寫下=B1+5.
這個時候就定義了這個單元格的值的運算規則。當B1格子填入資料後,這個格子的資料也就自動填充了,而且B1的值發生改變時,這個格子的值也會發生改變。
10.總結
- 1.業務邏輯本身就是使用事件來描述。Twitter,圖形化。。。
- 2.需要同時處理大量的IO操作。阻塞式IO需要同時使用大量線程,會導緻大量鎖之間的競争和太多的上下文切換。是以使用非阻塞性IO是一個更好的選擇。