线程池概念和原理:
线程池:容器--》集合(ArrayList,HashSet,LinkedList<Thread>,HashMap)
add(new Thread(xxx));
add(new Thread(xxx));
add(new Thread(xxx));
...
当线程第一次启动的时候,创建多个线程,保存到一个集合中
当我们想要使用线程的时候,我们就可以从集合中取出来线程使用
Thread t=list.remove(0);返回的是被移除的元素,(线程只能被一个任务使用)。
Thread t=linked.removeFist();
当我们使用完毕线程,需要把线程归还给线程池
list.add(t);
linked.addLast(t);
在JDK1.5之后,JDK内置了线程池,我们可以直接使用
线程池:其实就是一个容纳多个线程的容器,其中线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而増耗过多资源。
合理使用线程池的好处:
1.降低资源消耗
2.提高响应速度
3.提高线程的可管理性
线程池的代码实现:
/* * 线程池:JDK1.5之后提供的 * java.util.concurrent.Executors :线程池的工厂类,用来生成线程池 * Executors类中的静态方法: * static ExecutorService newFixedThreadPool(int nThreads) :创建一个线程池,该线程池重用固定数量的从共享无界队列中运行的线程。 * 参数: * int nThreads:创建线程池中包含的线程数量 * 返回值: * ExecutorService接口,返回的是ExecutorService接口的实现类对象,我们可以使用ExecutorService接口接收(面向接口编程) * java.util.concurrent.ExecutorService:线程池接口 * 用来线程池中获取线程,调用start方法,执行线程任务 * submit(Runnable task) :提交一个可运行的任务执行,并返回一个表示该任务的未来。 * 关闭/销毁线程池的方法: * void shutdown() * 线程池的使用步骤: * 1.使用线程池的工厂类Executors里边提供的静态方法newFixedThreadPool生产一个指定线程数量的线程池 * 2.创建一个类,实现Runnable接口,重写run方法,设置线程任务 * 3.调用ExecutorService中的方法submit,传递线程任务(实现类),开启线程,执行run方法 * 4.调用ExecutorService中的方法shutdown销毁线程池(不建议执行,不利于反复利用) * */ public class Demo01ThreadPool { public static void main(String[] args) { //1.使用线程池的工厂类Executors里边提供的静态方法newFixedThreadPool生产一个指定线程数量的线程池 ExecutorService es = Executors.newFixedThreadPool(2); //3.调用ExecutorService中的方法submit,传递线程任务(实现类),开启线程,执行run方法 es.submit(new RunnableImpl());//pool-1-thread-2创建了一个新的线程执行 //线程池会一直开启,使用完了线程会自动归还给线程池,线程可以继续使用 es.submit(new RunnableImpl());//pool-1-thread-1创建了一个新的线程执行 es.submit(new RunnableImpl());//pool-1-thread-1创建了一个新的线程执行 es.shutdown(); es.submit(new RunnableImpl());//抛异常,线程池没有了,不能获取线程了 } } /* * 2.创建一个类,实现Runnable接口,重写run方法,设置线程任务 * */ public class RunnableImpl implements Runnable { @Override public void run() { System.out.println(Thread.currentThread().getName()+"创建了一个新的线程执行"); } }
函数式编程思想概述:
面向对象的思想:
做一件事情,找一个能解决这个事情的对象,调用对象的方法,完成事情
函数式编程思想:
只要能获取到结果,谁去做的,怎么做的都不重要,重视的是结果,不重视过程。
冗余的Runnable代码:
/* * 使用实现Runnable接口的方式实现多线程程序 * */ public class Demo01Runnable { public static void main(String[] args) { //创建Runnable接口的实现类对象 RunnableImpl run=new RunnableImpl(); //创建Thread类对象,构造方法中传递Runnable接口的实现类 Thread t = new Thread(run); //调用start方法开启新线程,执行run方法 t.start(); //简化代码,使用匿名内部类实现多线程程序 Runnable r=new Runnable(){ @Override public void run() { System.out.println(Thread.currentThread().getName()+"新的线程创建了"); } }; new Thread(r).start(); //继续简化代码 new Thread(new Runnable(){ @Override public void run() { System.out.println(Thread.currentThread().getName()+"新的线程创建了"); } }).start(); } } /* * 创建Runnable接口的实现类,重写run方法,设置线程任务 * */ public class RunnableImpl implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName()+"新的线程创建了"); } }
只有方法体是最重要的,其余很多余。
编程思想转换&体验Lambda的更优写法:
我们真正的目的是:传递一段代码
public class Demo02Lambda { public static void main(String[] args) { //使用匿名内部类的方式实现多线程 new Thread(new Runnable(){ @Override public void run() { System.out.println(Thread.currentThread().getName()+"新的线程创建了"); } }).start(); //使用Lambda表达式实现多线程 new Thread(()-> { System.out.println(Thread.currentThread().getName()+"新的线程创建了"); } ).start(); } }
Lambda标准格式:
匿名内部类的好处和弊端:
一方面,匿名内部类帮我们省去了实现类的定义;另一方面,匿名内部类的语法太复杂。
/* * Lambda表达式的标准格式: * 由三部分组成: * a.一些参数 * b.一个箭头 * c.一段代码 * 格式: * (参数列表)->{一些重写的代码}; * 解释说明格式: * ():接口中抽象方法的参数列表,没有参数就空着,有参数就写出参数,多个参数使用,分割 * ->:传递的意思,把参数传递给方法体{} * {}:重写接口的抽象方法的方法体 * */
Lambda表达式的无参数无返回值练习:
/* * 需求: * 给定一个厨子Cook接口,内含惟一的抽象方法makeFood,且无参数,无返回值。 * 使用Lambda的标准格式调用invokeCook方法,打印出“吃饭啦!”字样。 * */ public interface Cook { //定义无参数无返回值的方法makeFood public abstract void makeFood(); } public class Demo03Cook { public static void main(String[] args) { //调用invokeCook方法,参数是Cook接口,传递Cook接口的匿名内部类对象 invokeCook(new Cook() { @Override public void makeFood() { System.out.println("吃饭啦!"); } }); //使用Lambda表达式,简化匿名内部类的书写 invokeCook(()->{ System.out.println("吃饭啦!"); }); } //定义一个方法,参数传递Cook接口,方法内部调用Cook接口中的方法makeFood public static void invokeCook(Cook cook){ cook.makeFood(); } }
Lambda表达式有参数有返回值的练习:
/* * Lambda表达式有参数有返回值的练习: * 需求: * 使用数组存储多个Person对象。 * 对数组中的Person对象使用Arrays的sort方法通过年龄进行升序排序 * */ public class Demo01Lambda { public static void main(String[] args) { //使用数组存储多个Person对象。 Person[] arr={ new Person("柳岩",20), new Person("迪丽热巴",18), new Person("古力娜扎",19), }; //对数组中的Person对象使用Arrays的sort方法通过年龄进行升序(前边-后边)排序 /*Arrays.sort(arr, new Comparator<Person>() { @Override public int compare(Person o1, Person o2) { return o1.getAge()-o2.getAge(); } });*/ //使用Lambda表达式简化匿名内部类 Arrays.sort(arr,(Person o1, Person o2) -> { return o1.getAge()-o2.getAge(); }); //遍历数组 for (Person p : arr) { System.out.println(p); } } }
Lambda表达式有参数有返回值的练习2:
/* * 需求: * 给定一个计算器Calculator接口,内含抽象方法calc可以将int数字相加的到和值 * 使用Lambda的标准格式调用invokeCalc方法,完成相加计算 * */ public interface Calculator { //定义一个计算两个int整数和的方法,并返回结果 public abstract int calc(int a,int b); } public class Demo01Calculator { public static void main(String[] args) { //调用invokeCalc方法,方法的参数是一个接口,可以使用匿名内部类 invokeCalc(10, 20, new Calculator() { @Override public int calc(int a, int b) { return a+b; } }); //使用Lambda表达式,简化匿名内部类的写法 invokeCalc(10,20,(int a,int b)->{ return a+b; }); } /* 定义一个方法 参数传递两个int类型的整数 参数传递Calculator接口 方法内部调用Calculator中的方法calc计算两个整数的和 */ public static void invokeCalc(int a,int b,Calculator c){ int sum=c.calc(a,b); System.out.println(sum); } }
Lambda省略格式&Lambda使用前提:
/* * Lambda表达式:是可推导,可省略的 * 凡是根据上下文推导出来的内容,都可以省略书写 * 可以省略的内容: * 1.(参数列表):括号中参数列表的数据类型,可以省略不写 * 2.(参数列表):括号中的参数如果只有一个,那么类型和括号都可以省略 * 3.{一些代码}:如果{}中的代码只有一行,无论是否有返回值,都可以省略({},return,分号)。 * 注意:{},return,分号必须一起省略 * */ public class Demo01ArrayList { public static void main(String[] args) { //JDK1.7之前,创建集合对象,必须把前后的泛型都写上 ArrayList<String> list01=new ArrayList<String>(); //JDK1.7版本之后,=后面的泛型可以省略,后面的泛型是可以根据前面的泛型推导出来的 ArrayList<String> list02=new ArrayList<>(); } } //使用Lambda表达式实现多线程 new Thread(()-> { System.out.println(Thread.currentThread().getName()+"新的线程创建了"); } ).start(); //优化省略Lambda new Thread(()-> System.out.println(Thread.currentThread().getName()+"新的线程创建了") ).start();
invokeCook(()->{ System.out.println("吃饭啦!"); }); //优化省略Lambda invokeCook(()-> System.out.println("吃饭啦!") );
//使用Lambda表达式简化匿名内部类 Arrays.sort(arr,(Person o1, Person o2) -> { return o1.getAge()-o2.getAge(); }); //优化省略Lambda Arrays.sort(arr,(o1, o2) -> o1.getAge()-o2.getAge());
//使用Lambda表达式,简化匿名内部类的写法 invokeCalc(10,20,(int a,int b)->{ return a+b; }); //省略Lambda invokeCalc(10,20,(a,b)->a+b);
Lambda的使用前提
1.使用Lambda必须具有接口,且要求接口中有且仅有一个抽象方法。
2.使用Lambda必须具有上下文判断。
有且仅有一个抽象方法的接口,称为“函数式接口”。