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的主体。
- 如果Lambda表达式中要执行多个语句块,需要将多个语句块以{}进行包装,如果有返回值,需要显示指定return语句,如下所示:
Parameters -> {expressions;};
- 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 编译LambdaTest2.java文件得到LambdaTest2.class
javac LambdaTest2.java
Admin@PS20190604VZCZ MINGW64 /d/test
$ dir
LambdaTest2.class LambdaTest2.java Print.class
- javap -p LambdaTest2.class
上面命令中的-p表示输出所有类及成员,运行上面的命令后,得的结果如下所示:
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 lambda0(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: 符号lambda0(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表达式里面的内容,那么又是如何调用的生成的私有静态函数(lambda0(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); };
}
}
``
网友评论