天天看點

java8使用Lambda表達式編寫并發

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.回調

java8使用Lambda表達式編寫并發
java8使用Lambda表達式編寫并發
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;
}      
java8使用Lambda表達式編寫并發

使用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是一個更好的選擇。

繼續閱讀