2019-11-06

2019-11-06  本文已影响0人  远飞的千纸鹤

1.1 为什么需要Lambda表达式?

使用Lambda表达式可以使代码变的更加紧凑,例如在Java中实现一个线程,只输出一个字符串Hello World!,我们的代码如下所示:

public static void main(String[] args) throws Exception {
    new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("Hello World!");
        }
    }).start();
    TimeUnit.SECONDS.sleep(1000);
}

使用Lambda表达式之后代码变成如下形式:

public static void main(String[] args) throws Exception {
    new Thread(() -> System.out.println("Hello World!")).start();
    TimeUnit.SECONDS.sleep(1000);
}

代码变的更紧凑了~

1.2 Lambda表达式使用形式

Parameters -> an expression
lambda运算符 " -> ",可以叫他,“转到”或者 “成为”,运算符将表达式
分为两部分,左边指定输入参数,右边是lambda的主体。
  1. 如果Lambda表达式中要执行多个语句块,需要将多个语句块以{}进行包装,如果有返回值,需要显示指定return语句,如下所示:
Parameters -> {expressions;};
  1. Lambda表达式不需要参数,可以使用一个空括号表示,如下示例所示 一下
() -> {for (int i = 0; i < 1000; i++) doSomething();};

3.如果Lambda表达式参数类型可以省略,由编译器推断出来

@FunctionalInterface
interface Function3 {
    Integer add(Integer a,Integer b);
}
public class LambdaTest {
    public static void main(String[] args) {
            Function1  func  =  (a,b) -> a + b;
    }
}

1.3 函数式接口

函数式接口是Java 8为支持Lambda表达式新发明的。
特点:
接口有且仅有一个抽象方法
允许定义静态方法
允许定义默认方法
允许java.lang.Object中的public方法
该注解不是必须的,如果一个接口符合"函数式接口"定义,那么加不加该注解都没有影响。加上该注解能够更好地让编译器进行检查。如果编写的不是函数式接口,但是加上了@FunctionInterface,那么编译器会报错粘贴代码

@FunctionalInterface
public interface MyInterface {

    //抽象方法
    public void hello();

    // java.lang.Object中的public方法
    public String toString();

    //默认方法
    public default void println(){
        System.out.println("hello world");
    }

    //静态方法
    public static void staticMethod(){

    }

}
public interface Iterable<T> {
    /**
     * Returns an iterator over elements of type {@code T}.
     *
     * @return an Iterator.
     */
    Iterator<T> iterator();

    /**
     * Performs the given action for each element of the {@code Iterable}
     * until all elements have been processed or the action throws an
     * exception.  Unless otherwise specified by the implementing class,
     * actions are performed in the order of iteration (if an iteration order
     * is specified).  Exceptions thrown by the action are relayed to the
     * caller.
     *
     * @implSpec
     * <p>The default implementation behaves as if:
     * <pre>{@code
     *     for (T t : this)
     *         action.accept(t);
     * }</pre>
     *
     * @param action The action to be performed for each element
     * @throws NullPointerException if the specified action is null
     * @since 1.8
     */
    default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }

在Java 8中向iterable这种接口增加一个方法,那么实现这个接口的所有类都要需实现一遍这个方法,那么Java 8需要更改的类就太多的,因此在Iterable接口里增加一个default实现,那么实现这个接口的所有类就都具有了这种实现,说白了,就是一个模板设计模式吧

1.4 方法引用

有时,我们需要执行的代码在某些类中已经存在,这时我们没必要再去写Lambda表达式,可以直接使用该方法,这种情况我们称之为方法引用

     List<Apple> apples = new ArrayList<Apple>(){
            {
                add(new Apple(1));
                add(new Apple(4));
                add(new Apple(2));
                add(new Apple(5));
                add(new Apple(7));
            }
        };
        //使用Lambda表达式对重量排序 对苹果按照重量从小到大排序
        apples.sort((a1,a2) -> a1.getWeight().compareTo(a2.getWeight()));


        //使用方法引用和java.util.Comparator.comparing对重量排序
        apples.sort(comparing(Apple :: getWeight));

        // 方法引用主要分为三类
        //(1)指向静态方法的方法引用;
        List<String> list2  = Arrays.asList("1", "3", "5", "7");
        List<Integer> numbers = list2.stream().map(Integer :: parseInt).collect(Collectors.toList());

        //(2)指向任意类型实例方法的方法引用;
        List<Integer> weights = apples.stream().map(Apple::getWeight).collect(Collectors.toList())
        //(3)构造函数引用
        String str = "test";
        Stream.of(str).map(String::new).peek(System.out::println).findFirst();

1.5 Lambda表达式作用域

public class LambdaScope {
    public static void main(String[] args) {
        String[] datas = new String[] {"peng","Zhao","li"};
        datas = null;
        new Thread(() -> System.out.println(datas)).start();
    }
  Integer i = 0;
  i++;
        new Thread(() -> System.out.println(i++)).start();

        AtomicInteger j = new AtomicInteger();
        j.set(0);
        new Thread(() -> System.out.println(j.getAndIncrement())).start();

这段代码会报datas不是final或者effectively final
What is effectively final?
Effectively final就是有效只读变量,意思是这个变量可以不加final关键字,但是这个变量必须是只读变量,即一旦定义后,在后面就不能再随意修改
Java中内部类以及Lambda表达式中也不允许修改外部类中的变量,这是为了避免多线程情况下的race condition竞争条件

1.6 Lambda实现原理分析

@FunctionalInterface
interface Print<T>{
    public void print(T t);
}

public class LambdaTest2 {
    public static void pringString(String s ,Print<String> print){
        print.print(s);
    }

    public static void main(String[] args){
        pringString("hello",x -> System.out.println(x));
    }
}

Lambda表达式经过编译之后,到底会生成什么东西呢?
大家可以猜想一下Lambda表达式是不是转化成与之对应的函数式接口的一个实现类呢,然后通过多态的方式调用子类的实现呢?又或者是匿名内部类的实现?

@FunctionalInterface
interface Print<T>{
    public void print(T t);
}
class PrintImpl implements Print<String> {
    @Override
    public void print(String x) {
        System.out.println(x);
    }
}
public class LambdaTest2 {
    public static void pringString(String s ,Print<String> print){
        print.print(s);
    }

    public static void main(String[] args){
        pringString("hello",new PrintImpl());
    }
}

匿名内部类

@FunctionalInterface
interface Print<T>{
    public void print(T t);
}
public class LambdaTest2 {
    public static void pringString(String s ,Print<String> print){
        print.print(s);
    }
    public static void main(String[] args){
        pringString("hello", new Print<String>() {
            @Override
            public void print(String s) {
                System.out.println(s);
            }
        });
    }
}

或者是这种

@FunctionalInterface
interface Print<T>{
    public void print(T t);
}

public class LambdaTest2 {
    public static void pringString(String s ,Print<String> print){
        print.print(s);
    }

  static final class PintImpl implements Print {
    public void print(String x) {
        staticMethod(x)
    }
        private PintImpl () {
        } 
    }

    private static void staticMethod(String s){
            System.out.println(s);
    }

    public static void main(String[] args){
        pringString("hello",new PintImpl() );
    }
}

上面的代码,除了在代码长度上长了点外,与用Lambda表达式实现的代码运行结果是一样的
带着这个疑问,我们来尝试用jdk的bin目录下的一个字节码查看工具及反编译工具
看下编译后的.class文件。

javac LambdaTest2.java
Admin@PS20190604VZCZ MINGW64 /d/test
$ dir
LambdaTest2.class  LambdaTest2.java  Print.class
D:\test>javap -p LambdaTest2.class
Compiled from "LambdaTest2.java"
public class test.LambdaTest2 {
  public test.LambdaTest2();
  public static void pringString(java.lang.String, test.Print<java.lang.String>);
  public static void main(java.lang.String[]);
  private static void lambda$main$0(java.lang.String);
}
D:\test>$ javap -p Print.class
Compiled from "LambdaTest2.java"
interface test.Print<T> {
  public abstract void print(T);
}

编译器会根据Lambda表达式生成一个私有的静态函数
private static void lambdamain0(java.lang.String);
验证一下,

package test;

@FunctionalInterface
interface Print<T>{
    public void print(T t);
}

public class LambdaTest2 {

    public static void pringString(String s ,Print<String> print){
        print.print(s);
    }

    private static void lambda$main$0(String s){

    }

    public static void main(String[] args){

        pringString("hello",x -> System.out.println(x));
    }
}

运行一下
Error:(14, 25) java: 符号lambdamain0(java.lang.String)与test.LambdaTest2中的 compiler-synthesized 符号冲突

D:\test>javac LambdaTest2.java
LambdaTest2.java:14: 错误: 符号lambda$main$0(String)与LambdaTest2中的 compiler-synthesized 符号冲突
    private static void lambda$main$0(String s){
                        ^
LambdaTest2.java:1: 错误: 符号lambda$main$0(String)与LambdaTest2中的 compiler-synthesized 符号冲突
package test;
^
2 个错误

有了上面的内容,可以知道的是Lambda表达式在Java 9中首先会生成一个私有的静态函数,这个私有的静态函数干的就是Lambda表达式里面的内容,那么又是如何调用的生成的私有静态函数(lambdamain0(String s))呢?
反编译一下LambdaTest.class 生成的字节码

D:\test>javap
用法: javap <options> <classes>
其中, 可能的选项包括:
  -help  --help  -?        输出此用法消息
  -version                 版本信息
  -v  -verbose             输出附加信息
  -l                       输出行号和本地变量表
  -public                  仅显示公共类和成员
  -protected               显示受保护的/公共类和成员
  -package                 显示程序包/受保护的/公共类
                           和成员 (默认)
  -p  -private             显示所有类和成员
  -c                       对代码进行反汇编
  -s                       输出内部类型签名
  -sysinfo                 显示正在处理的类的
                           系统信息 (路径, 大小, 日期, MD5 散列)
  -constants               显示最终常量
  -classpath <path>        指定查找用户类文件的位置
  -cp <path>               指定查找用户类文件的位置
  -bootclasspath <path>    覆盖引导类文件的位置

javap -p -v -c LambdaTest.class

D:\test>javap -p -v -c LambdaTest2.class
Classfile /D:/test/LambdaTest2.class
  Last modified 2019-11-10; size 1212 bytes
  MD5 checksum 9489e3d0504b929b245896c12faccf30
  Compiled from "LambdaTest2.java"
public class test.LambdaTest2
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #9.#24         // java/lang/Object."<init>":()V
   #2 = InterfaceMethodref #25.#26        // test/Print.print:(Ljava/lang/Object;)V
   #3 = String             #27            // hello
   #4 = InvokeDynamic      #0:#33         // #0:print:()Ltest/Print;
   #5 = Methodref          #8.#34         // test/LambdaTest2.pringString:(Ljava/lang/String;Ltest/Print;)V
   #6 = Fieldref           #35.#36        // java/lang/System.out:Ljava/io/PrintStream;
   #7 = Methodref          #37.#38        // java/io/PrintStream.println:(Ljava/lang/String;)V
   #8 = Class              #39            // test/LambdaTest2
   #9 = Class              #40            // java/lang/Object
  #10 = Utf8               <init>
  #11 = Utf8               ()V
  #12 = Utf8               Code
  #13 = Utf8               LineNumberTable
  #14 = Utf8               pringString
  #15 = Utf8               (Ljava/lang/String;Ltest/Print;)V
  #16 = Utf8               Signature
  #17 = Utf8               (Ljava/lang/String;Ltest/Print<Ljava/lang/String;>;)V
  #18 = Utf8               main
  #19 = Utf8               ([Ljava/lang/String;)V
  #20 = Utf8               lambda$main$0
  #21 = Utf8               (Ljava/lang/String;)V
  #22 = Utf8               SourceFile
  #23 = Utf8               LambdaTest2.java
  #24 = NameAndType        #10:#11        // "<init>":()V
  #25 = Class              #41            // test/Print
  #26 = NameAndType        #42:#43        // print:(Ljava/lang/Object;)V
  #27 = Utf8               hello
  #28 = Utf8               BootstrapMethods
  #29 = MethodHandle       #6:#44         // invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
  #30 = MethodType         #43            //  (Ljava/lang/Object;)V
  #31 = MethodHandle       #6:#45         // invokestatic test/LambdaTest2.lambda$main$0:(Ljava/lang/String;)V
  #32 = MethodType         #21            //  (Ljava/lang/String;)V
  #33 = NameAndType        #42:#46        // print:()Ltest/Print;
  #34 = NameAndType        #14:#15        // pringString:(Ljava/lang/String;Ltest/Print;)V
  #35 = Class              #47            // java/lang/System
  #36 = NameAndType        #48:#49        // out:Ljava/io/PrintStream;
  #37 = Class              #50            // java/io/PrintStream
  #38 = NameAndType        #51:#21        // println:(Ljava/lang/String;)V
  #39 = Utf8               test/LambdaTest2
  #40 = Utf8               java/lang/Object
  #41 = Utf8               test/Print
  #42 = Utf8               print
  #43 = Utf8               (Ljava/lang/Object;)V
  #44 = Methodref          #52.#53        // java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
  #45 = Methodref          #8.#54         // test/LambdaTest2.lambda$main$0:(Ljava/lang/String;)V
  #46 = Utf8               ()Ltest/Print;
  #47 = Utf8               java/lang/System
  #48 = Utf8               out
  #49 = Utf8               Ljava/io/PrintStream;
  #50 = Utf8               java/io/PrintStream
  #51 = Utf8               println
  #52 = Class              #55            // java/lang/invoke/LambdaMetafactory
  #53 = NameAndType        #56:#60        // metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
  #54 = NameAndType        #20:#21        // lambda$main$0:(Ljava/lang/String;)V
  #55 = Utf8               java/lang/invoke/LambdaMetafactory
  #56 = Utf8               metafactory
  #57 = Class              #62            // java/lang/invoke/MethodHandles$Lookup
  #58 = Utf8               Lookup
  #59 = Utf8               InnerClasses
  #60 = Utf8               (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
  #61 = Class              #63            // java/lang/invoke/MethodHandles
  #62 = Utf8               java/lang/invoke/MethodHandles$Lookup
  #63 = Utf8               java/lang/invoke/MethodHandles
{
  public test.LambdaTest2();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 8: 0

  public static void pringString(java.lang.String, test.Print<java.lang.String>);
    descriptor: (Ljava/lang/String;Ltest/Print;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=2, args_size=2
         0: aload_1
         1: aload_0
         2: invokeinterface #2,  2            // InterfaceMethod test/Print.print:(Ljava/lang/Object;)V
         7: return
      LineNumberTable:
        line 11: 0
        line 12: 7
    Signature: #17                          // (Ljava/lang/String;Ltest/Print<Ljava/lang/String;>;)V

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: ldc           #3                  // String hello
         2: invokedynamic #4,  0              // InvokeDynamic #0:print:()Ltest/Print;
         7: invokestatic  #5                  // Method pringString:(Ljava/lang/String;Ltest/Print;)V
        10: return
      LineNumberTable:
        line 16: 0
        line 17: 10

  private static void lambda$main$0(java.lang.String);
    descriptor: (Ljava/lang/String;)V
    flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: aload_0
         4: invokevirtual #7                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         7: return
      LineNumberTable:
        line 16: 0
}
SourceFile: "LambdaTest2.java"
InnerClasses:
     public static final #58= #57 of #61; //Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
BootstrapMethods:
  0: #29 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
    Method arguments:
      #30 (Ljava/lang/Object;)V
      #31 invokestatic test/LambdaTest2.lambda$main$0:(Ljava/lang/String;)V
      #32 (Ljava/lang/String;)V

注意反编译后main方法部分:
补充一下invokedynamic , invokestatic 指令

函数调用操作码 作用
invokestatic 调用类方法(静态绑定,速度快)
invokevirtual 指令调用一个对象的实例方法(动态绑定)
invokespecial 指令调用实例初始化方法、私有方法、父类方法。(静态绑定,速度快)
invokeinterface 调用引用类型为interface的实例方法(动态绑定)
invokedynamic JDK 7引入的,主要是为了支持动态语言的方法调用
  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: ldc           #3                  // String hello
     // 注意下面两句:通过实例调用 print
         2: invokedynamic #4,  0              // InvokeDynamic #0:print:()Ltest/Print;
    //调用静态方法 pringString
         7: invokestatic  #5                  // Method pringString:(Ljava/lang/String;Ltest/Print;)V
        10: return
      LineNumberTable:
        line 16: 0
        line 17: 10

编译器会在每个Lambda表达式出现的地方插入一条indy(invokedynamic)指令,同时还必须在class文件里生成相应的CONSTANT_InvokeDynamic_info常量池项和BootstrapMethods属性等信息。这些信息将这条indy指令的bootstrap方法指向LambdaMetafactory.metafactory(...)方法。

InnerClasses:
     public static final #58= #57 of #61; //Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
BootstrapMethods:
  0: #29 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
    Method arguments:
      #30 (Ljava/lang/Object;)V
      #31 invokestatic test/LambdaTest2.lambda$main$0:(Ljava/lang/String;)V
      #32 (Ljava/lang/String;)V

当JVM第一次执行到这条indy指令的时候,它会找到这条指令相应的bootstrap方法,然后调用该方法,得到一个CallSite。下面是metafactory()方法的代码

    /**
     * Facilitates the creation of simple "function objects" that implement one
     * or more interfaces by delegation to a provided {@link MethodHandle},
     * after appropriate type adaptation and partial evaluation of arguments.
     * Typically used as a <em>bootstrap method</em> for {@code invokedynamic}
     * call sites, to support the <em>lambda expression</em> and <em>method
     * reference expression</em> features of the Java Programming Language.
    *
    * <p>When the target of the {@code CallSite} returned from this method is
     * invoked, the resulting function objects are instances of a class which
     * implements the interface named by the return type of {@code invokedType},
     * declares a method with the name given by {@code invokedName} and the
     * signature given by {@code samMethodType}.  It may also override additional
     * methods from {@code Object}.
     * LambdaMetafactory方便我们创建简单的"函数对象",这些函数对象通过代理MethodHandle实现了一些        
     * 接口。
  * 当这个函数返回的CallSite被调用的时候,会产生一个类的实例
     */
  public static CallSite metafactory(MethodHandles.Lookup caller,
                                       String invokedName,
                                       MethodType invokedType,
                                       MethodType samMethodType,
                                       MethodHandle implMethod,
                                       MethodType instantiatedMethodType)
            throws LambdaConversionException {
        AbstractValidatingLambdaMetafactory mf;
        mf = new InnerClassLambdaMetafactory(caller, invokedType,
                                             invokedName, samMethodType,
                                             implMethod, instantiatedMethodType,
                                             false, EMPTY_CLASS_ARRAY, EMPTY_MT_ARRAY);
        mf.validateMetafactoryArgs();
        return mf.buildCallSite();
    }

在运行时加上-Djdk.internal.lambda.dumpProxyClasses,加上这个参数后,运行时,会将生成的内部类class码输出到一个文件中

D:\>java -Djdk.internal.lambda.dumpProxyClasses  test.LambdaTest2
hello
 D:\test 的目录

2019/11/10  19:25    <DIR>          .
2019/11/10  19:25    <DIR>          ..
2019/11/10  19:25               398 LambdaTest2$$Lambda$1.class
2019/11/10  18:53             1,212 LambdaTest2.class
2019/11/10  18:53               341 LambdaTest2.java
2019/11/10  18:53               296 Print.class
               4 个文件          2,247 字节
               2 个目录 100,959,272,960 可用字节

反编译一下LambdaTest2$$Lambda$1.class,内容如下

D:\test>javap -p LambdaTest2$$Lambda$1.class
final class test.LambdaTest2$$Lambda$1 implements test.Print {
  private test.LambdaTest2$$Lambda$1();
  public void print(java.lang.Object);
}
D:\test>javap -c -p LambdaTest2$$Lambda$1.class
final class test.LambdaTest2$$Lambda$1 implements test.Print {
  private test.LambdaTest2$$Lambda$1();
    Code:
       0: aload_0
       1: invokespecial #10                 // Method java/lang/Object."<init>":()V
       4: return

  public void print(java.lang.Object);
    Code:
       0: aload_1
       1: checkcast     #15                 // class java/lang/String
       4: invokestatic  #21                 // Method test/LambdaTest2.lambda$main$0:(Ljava/lang/String;)V
       7: return
}

代码还原

package test;

@FunctionalInterface
interface Print<T>{
    public void print(T t);
}

public class LambdaTest2 {
    public static void pringString(String s ,Print<String> print){
        print.print(s);
    }

  static final class LambdaTest2$$Lambda$1 implements Print {
        public void print(Object obj) {
            LambdaTest2.lambda$main$0((String) obj);
        }
        private LambdaTest2$$Lambda$1() {
        }
    }

    private static void lambda$main$0(String s){
            System.out.println(s);
    }

    public static void main(String[] args){
        pringString("hello",new LambdaTest2$$Lambda$1() );
    }
}

至此,lambda表达式的脱糖过程已经了解完成。
为什么要绕一圈生成一个内部类的动态调用点然后执行,而不是直接把lambda编译成内部类,也许是为了减少编译后的文件数

1.7 Lambda在智能系统中的使用场景

接口只有查询操作,通常会优先读从库,调用ThreadLocalUtils工具类里面的runWithSecondaryPreferredMode方法。看下这里对Runnable函数式接口的使用。

Service
public class HistoryApiServiceImpl extends HistoryApiService {
    private static final Logger LOGGER = LoggerFactory.getLogger(HistoryApiServiceImpl.class);

    @Autowired
    private HistoryService historyService;

    @Override
    public Response getCustomerFollowupList(CustomerFollowUpReq body, SecurityContext securityContext, HttpServletRequest request) throws NotFoundException {
        String agentId = checkAgent(request);
        CustomerFollowUps customerFollowUps = ThreadLocalUtils.runWithSecondaryPreferredMode(() ->historyService.getCustomerFollowupList(body,agentId));
        return ResponseHelper.ok(customerFollowUps);
    }

    @Override
    public Response getMLSHouseList(UnitCondition body, SecurityContext securityContext, HttpServletRequest request) throws NotFoundException {
        String agentId = checkAgent(request);
        MLSHouseListRsp rsp =  ThreadLocalUtils.runWithSecondaryPreferredMode(() -> historyService.getMLSHouseList(body, agentId));
        return ResponseHelper.ok(rsp);
    }

    @Override
    public Response getMlsHouseFollowUpList(FollowUpReq body, SecurityContext securityContext, HttpServletRequest request) throws NotFoundException {
        String agentId = checkAgent(request);
        FollowUpRsp followUpRsp = ThreadLocalUtils.runWithSecondaryPreferredMode(() -> historyService.getMlsHouseFollowUpList(body,agentId));
        return ResponseHelper.ok(followUpRsp);
    }

ThreadLocalUtils

package com.fanggeek.common.utils.thread;

import com.fanggeek.common.alarm.model.RequestInfo;
import java.util.function.Supplier;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ThreadLocalUtils {

    public static void runWithSecondaryPreferredMode(Runnable runnable) {
        try {
            getSomeSwitch().turnOnSecondaryPreferred();
            runnable.run();
        } catch (Exception var5) {
            LOGGER.error("runWithSecondaryPreferredMode Runnable", var5);
            throw var5;
        } finally {
            getSomeSwitch().turnOffSecondaryPreferred();
        }

    }

    public static <T> T runWithSecondaryPreferredMode(Supplier<T> xxx) {
        Object t = null;

        try {
            getSomeSwitch().turnOnSecondaryPreferred();
            t = xxx.get();
        } catch (Exception var6) {
            LOGGER.error("runWithSecondaryPreferredMode Supplier ", var6);
            throw var6;
        } finally {
            getSomeSwitch().turnOffSecondaryPreferred();
        }

        return t;
    }

    public static void runWithStringValue(Runnable runnable, String value) {
        try {
            setString(value);
            runnable.run();
        } catch (Exception var6) {
            LOGGER.error("runWithStringValue Runnable {}", ExceptionUtils.getStackTrace(var6));
            throw var6;
        } finally {
            stringRemove();
        }
    }

    public static <T> T runWithStringValue(Supplier<T> xxx, String value) {
        Object t = null;

        try {
            setString(value);
            t = xxx.get();
        } catch (Exception var7) {
            LOGGER.error("runWithStringValue Runnable {}", ExceptionUtils.getStackTrace(var7));
            throw var7;
        } finally {
            stringRemove();
        }

        return t;
    }
}
 *
 * @author  Arthur van Hoff
 * @see     java.lang.Thread
 * @see     java.util.concurrent.Callable
 * @since   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();
}
D:\test2>javap -p RunableTest.class
Compiled from "RunableTest.java"
public class test2.RunableTest {
  public test2.RunableTest();
  public static void main(java.lang.String[]);
  private static void lambda$main$0();
}

D:\test2>javap -p RunableTest$$Lambda$1.class
final class test2.RunableTest$$Lambda$1 implements java.lang.Runnable {
  private test2.RunableTest$$Lambda$1();
  public void run();
}

D:\test2>
package test2;

public class RunableTest {

    public static void main(String[] args){
        Runnable runnable= () -> System.out.println("helloword");
        runnable.run();
    }
}

Runnable接口常用于创建一个线程,不需要传递参数,也没有返回结果。
智能系统中,异步去执行某个操作,不需要返回结果可以用AsyncUtils.run方法

package java.util.function;

/**
 * Represents a supplier of results.
 *这是供给型函数接口.
 * <p>There is no requirement that a new or distinct result be returned each
 * time the supplier is invoked.
 *特点:
 * (1)只有返回值
 * (2)没有输入参数
 * get()方法被调用时,对于一定要new出一个新对象 or 生成一个和之前结果不同的值 这两方面,都没有强制规 
  *定.
 * 这一接口函数的功能方法为:get()
 * <p>This is a <a href="package-summary.html">functional interface</a>
 * whose functional method is {@link #get()}.
 * @param <T> the type of results supplied by this supplier
 *
 * @since 1.8
 */
@FunctionalInterface
public interface Supplier<T> {

    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();
}
package test3;

import java.util.concurrent.Callable;

public class CallableTest {

    public static void main(String[] args) throws Exception {
        Callable<Integer> callable= () -> new Integer(1);
        System.out.println(callable.call());
    }
}
D:\test3>javap -p CallableTest.class
Compiled from "CallableTest.java"
public class test3.CallableTest {
  public test3.CallableTest();
  public static void main(java.lang.String[]) throws java.lang.Exception;
  private static java.lang.Integer lambda$main$0() throws java.lang.Exception;
}

D:\test3>javap -p CallableTest$$Lambda$1.class
final class test3.CallableTest$$Lambda$1 implements java.util.concurrent.Callable {
  private test3.CallableTest$$Lambda$1();
  public java.lang.Object call();
}

重新放盘业务的使用

    private void modifyHouse4ReUpload(TeamUnitDocument teamUnitDoc) {
        //在当前线程中设置一个重新放盘的标识
        ThreadLocalUtils.runWithStringValue(() -> teamHouseV2Service.modifyHouse(teamUnitDoc), TeamConstant.REUPLOAD_THREAD_NAME);
    }

Null工具类中Supplier<T>函数式接口的使用

Null.ofInt(() -> body.getPrivateState())

public class Null {
public static final <T> T ofInt(Supplier<T> expr){
        Supplier<T> defaultValues =  ()-> (T)new Integer(0);
        return of(expr, defaultValues);
}

public static final <T> T ofDouble(Supplier<T> expr){
        Supplier<T> defaultValues =  ()-> (T)new Double(0.0D);
        return of(expr, defaultValues);
    }   
    
public static final <T> T ofLong(Supplier<T> expr){
        Supplier<T> defaultValues =  ()-> (T)new Long(0L);
                return of(expr, defaultValues);
      }

public static final <T> T of(Supplier<T> expr, Supplier<T> defaultValue){
        try{
            T result = expr.get();
            
            if(result == null){
                return defaultValue.get();
            }
            return result;
        }catch (NullPointerException|IndexOutOfBoundsException e) {
            return defaultValue.get();
        }catch (Exception e) {
            log.error("ObjectHelper get error.", e);
            throw new RuntimeException(e);
        }
}   
}

AsyncUtils

public class AsyncUtils {
    
    private static final Logger LOGGER =LoggerFactory.getLogger(AsyncUtils.class);
    
    private static final ExecutorService EXECUTOR_SERVICE_SINGLE = (ExecutorService) Executors.newSingleThreadExecutor();
    
    private static final ScheduledExecutorService EXECUTOR_SERVICE_SCHEDULED = Executors.newScheduledThreadPool(20); 
    
    private static final ExecutorService EXECUTOR_SERVICE_ACTION_LOG = (ExecutorService) Executors.newFixedThreadPool(2);

    /*
    房源列表专用,线程池阻塞队列
     */
    private static final LinkedBlockingQueue<Runnable> HOUSE_LIST_WAITING_QUEUE = new LinkedBlockingQueue<>();

    /**
     * <br>线程池(房源列表专用),目前大小为 10
     */
    public static final ExecutorService HOUSE_LIST_EXECUTOR_SERVICE = new ThreadPoolExecutor(10, 20,
            30000, TimeUnit.MILLISECONDS, HOUSE_LIST_WAITING_QUEUE);
    
    /**
     * 因为 Runnable 接口是 函数式接口(可以显式的加上@FunctionalInterface, 也可以不加,但是这个接口有且仅有一个方法)
     * <br>所以可以通过 lambda 表达式来快速构造 () -> xxxx
     * @param task
     */
    public static void run(Runnable task){
        AsyncExecutor.run(task);
    }
    
    public static void runActionLog(Runnable task){
        EXECUTOR_SERVICE_ACTION_LOG.execute(task);
    }
    
    public static <T> Future<T> submit(Callable<T> task){
        return AsyncExecutor.submit(task);
    }

AsyncExecutor

/**
 * 异步任务 执行器
 * @author YellowTail
 * @since 2019-04-24
 */
public class AsyncExecutor {
    
    /**
     * <br>异步任务提交后,都是在这个 链式 阻塞队列里 存着
     */
    private static final LinkedBlockingQueue<Runnable> TASK_WAITING_QUEUE = new LinkedBlockingQueue<>();
    
    /**
     * <br>定长线程池,目前大小为 10
     */
    public static final ExecutorService EXECUTOR_SERVICE = new ThreadPoolExecutor(10, 10,
            0L, TimeUnit.MILLISECONDS, TASK_WAITING_QUEUE);
    
    /**
     * <br>long 类型的 原子计数器,inc 操作基于 CAS
     */
    private static final AtomicLong TOTAL_COUNT = new AtomicLong();

    public static void run(Runnable task){
        TOTAL_COUNT.incrementAndGet();
        
        EXECUTOR_SERVICE.execute(task);
    }
    
    public static <T> Future<T> submit(Callable<T> task){
        TOTAL_COUNT.incrementAndGet();
        
        return EXECUTOR_SERVICE.submit(task);
    }

Callable接口

/**
 * A task that returns a result and may throw an exception.
 * Implementors define a single method with no arguments called
 * {@code call}.
 *
 * <p>The {@code Callable} interface is similar to {@link
 * java.lang.Runnable}, in that both are designed for classes whose
 * instances are potentially executed by another thread.  A
 * {@code Runnable}, however, does not return a result and cannot
 * throw a checked exception.
 *
 * <p>The {@link Executors} class contains utility methods to
 * convert from other common forms to {@code Callable} classes.
 *
 * @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;
}

Supplier<T>和Callable<V>接口都是 参数为空,返回一个结果
One basic difference between the 2 interfaces is that Callable allows checked exceptions to be thrown from within the implementation of it, while Supplier doesn't.

@FunctionalInterface
public interface Predicate<T> {

    /**
     * Evaluates this predicate on the given argument.
     *
     * @param t the input argument
     * @return {@code true} if the input argument matches the predicate,
     * otherwise {@code false}
     */
    boolean test(T t);
}
@FunctionalInterface
public interface Function<T, R> {

    /**
     * Applies this function to the given argument.
     *
     * @param t the function argument
     * @return the function result
     */
    R apply(T t);
}
@FunctionalInterface
public interface Consumer<T>{
 //接受 t 对象,无返回值
 void accept(T t);
    
//默认的组合方法,参数和返回值都是 Consumer 类型,先调用自己的 accept()方法,再调用参数的 accept()方法
 default Consumer<T>  andThen(Consumer<T>  after) {
 Objects.requireNonNull(after);
 return (T t) -> { accept(t); after.accept(t); };
 }
}
``
上一篇下一篇

猜你喜欢

热点阅读