1).什么是“线程池”:指封装了很多的“线程对象”的一个容器。
2).作用:内部的这些线程对象可以被“重复使用”;
线程对象每次使用完成后,会自动成为垃圾,不能再次的start。如果想要再次使用就需要再创建一个线程对象,但创建一个线程对象需要很大的系统开销,所以如果需要大量的、反复的执行同一线程会降低程序的效率。所以提出了“线程池”的概念。
public class Demo {
public static void main(String[] args) {
/*MyThread t1 = new MyThread();//需要5秒
t1.start();//鼓掌...
//过一会,还需要鼓掌的线程
t1 = new MyThread();//需要5秒
t1.start();//再次鼓掌....*/
//使用线程池;
//1.获取一个线程池对象;
ExecutorService service = Executors.newFixedThreadPool(20);//表示创建一个可以容纳2个核心的线程的"线程池"对象
//2.让线程池去执行,并缓存一个线程对象
MyThread t = new MyThread();//需要5秒
service.submit(t);//被第一次执行...
//3.重复执行之前的线程
System.out.println("再次执行鼓掌线程.....");
service.submit(t);//被第二次执行
}
}
1).之前我们学了两种实现线程的方式:
1).继承自Thread,重写run()
2).实现Runnable接口,重写run()
注意:上面两种方式都是run(),而这个run()方法有个缺点:不能返回值
如果我们希望“线程中”为主线程返回一些数据,之前的两种方式是做不到的。
2).从JDK5开始,又提供了实现线程的第三种方式:实现Callable接口,重写call()方法(相当于run()).
但这个方法可以返回值。 注意:Callable接口的方式实现线程,需要“线程池”来运行;
3).Callable接口与之前的两种方式的重要区别:Callable接口的方式可以返回值
3).示例代码:
/*
制作一个线程,这个计算1--100的累加和,并将结果返回给主线程
*/
public class MyCallable implements Callable {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 1; i <= 100 ; i++) {
sum += i;
}
return sum;
}
}
测试类:
public class Demo {
public static void main(String[] args)
throws ExecutionException, InterruptedException {
//1.创建一个线程池对象
ExecutorService service = Executors.newFixedThreadPool(2);
//2.创建MyCallable对象
MyCallable myCall = new MyCallable();
//3.让线程池去执行这个线程
Future future = service.submit(myCall);
Integer result = future.get();//获取结果
System.out.println("主线程收到的结果是:" + result);
}
}
1).示例代码:
1).定义一个接口:
public interface Animal {
public void eat();
}
1).面向对象的方式:定义类,并创建对象,传递实参,调用方法;
2).Lambda:不需要制作类,不需要创建对象,直接传递一个“方法”作为实参;
可以节省代码的编写。
fun(()->{
System.out.println("小猪吃草...");
}
);
1).必须要有一个“接口”;
2).接口中有、且只有一个“抽象方法”(可以定义参数、返回值) 这是函数式接口
1).必须有、且只有一个抽象方法;
2).其它静态方法、默认方法有没有、有多少都无所谓
3).Lambda几种常见的形式:
//1.作为形参:
public static void main(String[] args) {
//1.调用方法,传递实参时–前提:方法的形参必须是:接口,而且里面有,且只有一个抽象方法
fun(()->{
System.out.println(“小猪吃草…”);
});
//2.声明变量:
Animal a = ()-> {
System.out.println("小猪吃草...");
};
//3.作为返回值
Animal animal = getAnimal();
animal.eat();
}
public static void fun(Animal a){
a.eat();
}
public static Animal getAnimal(){
/*return new Animal() {
@Override
public void eat() {
System.out.println("小猪吃草...");
}
};*/
return () ->{
System.out.println("小猪吃草...");
};
}
//1.定义一个接口,接口中抽象方法是:带参、带返回值
public interface MyMath {
public int calc(int a,int b);
}
//2.调用
public class Demo {
public static void main(String[] args) {
//1.匿名内部类
fun(new MyMath() {
@Override
public int calc(int a, int b) {
return a + b;
}
},10,20);
//2.使用Lambda–由于接口中的方法是带参、带返回值的,所以导致Lambda也要带参,带返回值
fun((int a, int b)-> {
return a * b;
},10,20);
}
public static void fun(MyMath myMath,int m,int n) {
int r = myMath.calc(m,n);
System.out.println("结果是:" + r);
}
}
1).形参的“数据类型”都可以省略 fun((a, b)-> {
return a * b;
},10,20);
2).如果只有一个形参,可以同时省略:数据类型,一对小括号;
(如果省略小括号,必须省略数据类型)
1).定义一个接口:
public interface MyMath2 {
public void calc(int a);
}
2).测试类:
public class Demo {
public static void main(String[] args) {
fun( a ->{//形参只有一个,可以同时省略:小括号和数据类型
System.out.println(a);
},10);
}
public static void fun(MyMath2 myMath2,int m){
myMath2.calc(m);
}
}
2).关于Lambda表达式的方法体:
1).如果方法体中只有一句话:可以同时省略:一对大括号,return关键字,这条语句后面的分号
要省全省,要用全用
示例一:
1).定义一个接口:
public interface MyMath2 {
public void calc(int a);
}
2).测试类:
public class Demo {
public static void main(String[] args) {
fun( a ->//形参只有一个,可以同时省略:小括号和数据类型
System.out.println(a)//可以同时省略:大括号,语句后的分号
,10);
}
public static void fun(MyMath2 myMath2,int m){
myMath2.calc(m);
}
}
示例二:有参,有返回值:
1).定义一个接口:
public interface MyMath2 {
public int calc(int a,int b);
}
2).测试类:
public class Demo {
public static void main(String[] args) {
//完整格式
fun( (int a,int b) ->{
return a + b;
},10,20);
//省略格式
fun((a,b) -> a + b,10,20);
}
public static void fun(MyMath2 myMath2,int m,int n){
int r = myMath2.calc(m,n);
System.out.println(r);
}
}
-------------------------------------------------------------
小结省略规则:
1).形参:数据类型可以省略
2).形参:如果只有一个形参,可以同时省略:小括号,数据类型;
3).方法体:如果只有一句话,可以同时省略:大括号,return关键字,词条语句后的分号。
1).解开类和类之间的耦合
1).有,且只有一个抽象方法的接口,叫:函数式接口;
2).通常为了使用Lambda表达式,所以经常会特意的定义一些“函数式接口”。
3).为了避免定义函数式接口错误,Java为我们提供了一个“注解”,它类似于"@Override"注解,给编译器看的,
让编译器为我们检查这个接口是否是一个合格的函数式接口。
注解名:@FactionalInterface
4).示例代码:
@FunctionalInterface//作用:保证我们定义的接口是一个合格的函数式接口
public interface Animal {
public void eat();
}
public class Demo {
public static void main(String[] args) {
//1.使用匿名子类对象
fun(new Supplier() {
@Override
public String get() {
return "HelloWorld!";
}
});
``
//2.使用Lambda
fun(()->{return "HelloWorld!";});
}
//下面的方法预先定义好的
//此方法需要一个Supplier的子类对象,为fun()返回一个String对象
public static void fun(Supplier s){
String s = s.get();
System.out.println(s);
}
}
1).抽象方法原型:
void accept(T t);
示例代码:
public class Demo {
public static void main(String[] args) {
//1.使用匿名内部类调用
fun(new Consumer() {
@Override
public void accept(String s) {
System.out.println("转换为大写输出:" + s.toUpperCase());
}
},"Hello");
//2.使用Lambda表达式
fun(t -> System.out.println("转换为小写:" + t.toLowerCase()), "Hello");
}
//此方法需要一个Consumer和一个String
//此方法内部会将String传给Consumer,子类决定做什么
public static void fun(Consumer c,String s){
c.accept(s);
}
}
2).默认方法:
andThen()
作用:当需要连续的两次处理同一数据时,可以使用这个方法
示例代码:
public class Demo {
public static void main(String[] args) {
fun(s-> System.out.println("转换为大写:" + s.toUpperCase()),
s -> System.out.println("转换为小写:" + s.toLowerCase()),
"HelloWorld");
}
//andThen
public static void fun(Consumer c1,Consumer c2,String str){
//先调用c1.accept()然后再调用c2.accept()
/* c1.accept(str);
c2.accept(str);
*/
//等同于上面的写法
c1.andThen(c2).accept(str);//内部就是先调用c1.accept()然后再调用c2.accept()
}
}
1).抽象方法原型:
R apply(T t):接收第一个泛型类型,返回第二个泛型类型
通常是用于做“转换”。
2).默认方法:
andThen:当需要将一个数据进行两次连续的转换时,可以使用此方法。
示例代码:
public class Demo {
public static void main(String[] args) {
fun(s -> Integer.parseInt(s) + 10,n -> Integer.toString(n),"25");
}
//andThen:将一个String转换为Integer,然后 + 10 ,然后再转换为String
public static void fun(Function f1,Function f2,String age){
/*Integer intAge = f1.apply(age);
String str = f2.apply(intAge);*/
String str = f1.andThen(f2).apply(age);
System.out.println("字符串的结果:" + str);
}
}
boolean test(T t);
示例代码:
public class Demo {
public static void main(String[] args) {
//1.匿名内部类
fun(new Predicate() {
@Override
public boolean test(String s) {
return s.length() >= 5;
}
}, "Hell");
//2.使用Lambda
fun(s -> s.length() >= 5,"Hell");
}
public static void fun(Predicate p,String s){
boolean b = p.test(s);
System.out.println("判断的结果:" + b);
}
}
2).默认方法:
1).and():可以将两个Predicate的判断做逻辑与的判断;
public class Demo {
public static void main(String[] args) {
//验证一个字符串是否即包含大写的H,又包含大写的W
fun(s->s.contains("H"),s-> s.contains("W"),"HelloWorld");
}
//定义一个方法,可以对一个字符串进行两个判断,可以打印:两个判断的"并且"的结果
public static void fun(Predicate p1,Predicate p2,String s){
/*boolean b1 = p1.test(s);
boolean b2 = p2.test(s);
System.out.println(b1 && b2);*/
//等同于上面的效果
boolean b2 = p1.and(p2).test(s);
}
}
2).or():可以将两个Predicate的判断做逻辑或的判断;
3).negate():将一个Predicate的判断“取反”:
01.能够描述Java中线程池运行原理
1).可以缓存多个“线程对象”,并且可以将这些“线程对象”进行“重用”,这样可以大大降低创建线程对象的次数,提高程序的运行效率。
02.能够理解函数式编程相对于面向对象的优点
1).不用定义类,不用创建对象,直接传递方法体;代码简洁
03.能够掌握Lambda表达式的标准格式
1).一个小括号:形参
2).一个右箭头:语法
3).一对大括号:方法体
04.能够掌握Lambda表达式的省略格式与规则
省略规则:
1).形参:形参类型都可以省略;
2).形参:如果只有一个形参,可以同时省略:小括号,数据类型;
3).方法体:如果方法体中只有一句话,可以同时省略:大括号,return,分号。
05.能够明确Lambda的两项使用前提
1).必须是接口;
2).必须是“函数式接口”–有,且只有一个抽象方法。
06.能够使用Supplier函数式接口
1).生产者接口:没有参数,只有返回值
2).
public static void main(String[] args) {
//1.使用匿名子类对象
fun(new Supplier() {
@Override
public String get() {
return “HelloWorld!”;
}
});
//2.使用Lambda
fun(()->{return “HelloWorld!”;});
}
public static void fun(Supplier s){
String str = s.get();
System.out.println(str);
}
07.能够使用Consumer函数式接口
1).消费者接口:只有参数,没有返回值
public static void main(String[] args) {
//1.使用匿名内部类调用
fun(new Consumer() {
@Override
public void accept(String s) {
System.out.println(“转换为大写输出:” + s.toUpperCase());
}
},“Hello”);
//2.使用Lambda表达式
fun(t -> System.out.println("转换为小写:" + t.toLowerCase()), "Hello");
}
//此方法需要一个Consumer和一个String
//此方法内部会将String传给Consumer,子类决定做什么
public static void fun(Consumer c,String s){
c.accept(s);
}
08.能够使用Function函数式接口
1).函数接口:有入、有出
public class Demo {
public static void main(String[] args) {
//1.使用匿名内部类
fun(new Function
@Override
public String apply(Integer value) {
return value.toString();
}
},25);
//2.使用Lambda
fun(a -> a.toString(),25);
}
//此方法需要一个具有Integer和String泛型的Function子类对象
//方法内部调用Function子类的apply,接收一个Integer,返回一个String
public static void fun(Function fun,int age){
String s = fun.apply(age);
System.out.println("字符串的值:" + s);
}
}
09.能够使用Predicate函数式接口
1).判断接口:接收一个数据,返回一个boolean
public static void main(String[] args) {
//1.匿名内部类
fun(new Predicate() {
@Override
public boolean test(String s) {
return s.length() >= 5;
}
}, “Hell”);
//2.使用Lambda
fun(s -> s.length() >= 5,"Hell");
}
public static void fun(Predicate p,String s){
boolean b = p.test(s);
System.out.println(“判断的结果:” + b);
}