02-线程池、Lambda、Stream
线程、线程池、Lambda表达式、Stream流
1、线程
1.1、线程通信
1.1.1、概念
- 多线程通过共享数据和线程相关API对线程执行过程的一定控制
- Object类的等待和唤醒方法
- 注意
- 必须使用锁对象搭配同步synchronized代码块使用
1.1.2、sleep和wait的区别
- sleep睡眠时不会释放锁
- wait等待时会释放锁
1.1.3、线程通信案例-生产者消费者
-
实现步骤
-
- 定义一个集合,用于存放包子
- 创建厨师线程,生产包子
- 创建吃货线程,消费包子
- 启动线程
-
demo:
-
package Day09.ClassicCase._01多线程深入.ClassroomPractice.demo02线程通信案例; import java.util.ArrayList; /* 目标: 包子铺生产一个包子,吃货吃一个包子,包子铺再生产一个包子, 吃货再吃一个包子.包子铺再生产一个包子,吃货再吃一个包子. 讲解: 分析: 厨师(生产者) 如果桌子上有包子就唤醒吃货并且自己等待。 如果桌子上没有包子就生产并且唤醒吃货。
吃货(消费者) 如果桌子上没有包子就唤醒厨师并且自己等待。 如果桌子上有包子吃掉并且唤醒厨师。 实现步骤: 1.定义一个集合.用于存放包子 2.创建厨师线程,生产包子 3.创建吃货线程,消费包子 4.启动线程
*/
public class Demo02 {
public static void main(String[] args) {
// 1.定义一个集合.用于存放包子
ArrayListlist = new ArrayList<>(); // 把list作为共享对象 // 2.创建厨师线程,生产包子 new Thread(new Runnable() { @Override public void run() { int num = 0; while (true) { synchronized (list) { // 如果包子数量为0,那么就生产包子,并唤醒吃货线程 if (list.size() == 0) { list.add("包子" + num); num ++; System.out.println("厨师生产了:" + list); list.notify(); } else { // 如果此时已经有包子,那么厨师线程需要唤醒吃货并等待 list.notify(); try { list.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } }).start(); // 3.创建吃货线程,消费包子 new Thread(new Runnable() { @Override public void run() { while (true) { synchronized (list) { // 如果此时没有包子,那么吃货线程需要等待,并唤醒厨师线程生产 if (list.size() == 0) { list.notify(); try { list.wait(); } catch (InterruptedException e) { e.printStackTrace(); } }else { //如果此时有包子,那么吃货线程需要吃完,再叫醒厨师线程 String baoZi = list.remove(0); System.out.println("吃货吃了:" + baoZi); list.notify(); } } } } }).start(); // 4.启动线程 }
}
-
-
1.2、线程生命周期
1.2.1、线程状态的转换
1.2.2、线程的六个状态
- 新建状态(NEW)
- 可运行状态(RUNNABLE)
- 阻塞状态(BLOCKED)
- 无限等待状态(WAITTING)
- 计时等待(TIMED_WAITING)
- 终止状态(TERMINATED)
1.3、死锁
1.3.1、概念
- 死锁是指多线程在执行过程中竞争锁而造成的互相等待对方所的现象
1.3.2、产生条件
- 多个线程
- 多把锁
- 多个同步代码块嵌套
1.3.3、如何避免死锁
- 去掉其中一个条件就可以避免死锁
2、线程池
2.1、线程池的概念和好处
2.1.1、线程池概念
- 线程池是一个容器,可以保存一些长久存货的线程对象,负责创建、复用、管理线程
2.1.2、线程池的优势
- 提高响应速度,减少了创建新线程的时间
- 降低资源消耗,重复利用线程池中的线程,不需要每次都创建和销毁
- 便于线程管理,线程池可以几种管理并发线程的数量
2.2、线程池的使用方式(ExecutorService)
- Executors工具类创建线程池
- ExecutorsService线程池中的常用方法
2.2.1、使用方式1----提交Runnable任务
- 提交Runnable任务的使用步骤
- 创建线程池
- 创建Runnable任务
- 提交任务
2.2.2、使用方式2----提交Callable任务
-
Callable概述
-
public interface Callable<V> { V call() throws Exception;}
-
-
提交Callable任务的使用步骤
- 创建线程池
- 创建Callable任务
- 提交任务
-
demo
-
package Day09.ClassicCase._01多线程深入.ClassroomPractice.demo08线程池练习_重点; import java.util.concurrent.*; /* 目标:使用线程池方式执行任务,返回1-n的和 分析: 因为要返回1-到n的和,有返回值,使用Callable任务 */ public class Demo08 { public static void main(String[] args) throws ExecutionException, InterruptedException { // 创建线程池 ExecutorService executorService = Executors.newFixedThreadPool(3); // 创建Callable任务,计算1到n的和 SumCallable sumCallable = new SumCallable(100); // 提交任务 // 获取返回值 Future<Integer> future = executorService.submit(sumCallable); Integer integer = future.get(); System.out.println("Sum = " + integer); // 关闭线程池 executorService.shutdown(); } } class SumCallable implements Callable<Integer> { private int n; public SumCallable(int n) { this.n = n; } public SumCallable() { } @Override public Integer call() throws Exception { int sum = 0; for (int i = 1; i <= n; i++) { sum += i; } return sum; } }
-
2.3、ThreadPoolExecutor创建线程池(面试专用)
2.3.1、ThreadPoolExecutor API介绍
2.3.2、新任务拒绝策略
2.3.3、注意事项
- 临时线程什么时候创建
- 新任务提交的时候发现核心线程都在忙,任务队列也满了,并且还可以创建临时线程,此时才会创建临时线程
- 什么时候开始拒绝服务
- 当核心线程和临时线程都在忙,任务队列也满了,新的任务过来的时候才会开始任务拒绝
2.3.4、示例代码
-
package Day09.ClassicCase._01多线程深入.ClassroomPractice.demo09ThreadPoolExecutor创建线程池; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; public class Demo09 { /* public ThreadPoolExecutor( int corePoolSize, // 指定线程池的线程数量(不能小于0) int maximumPoolSize, // 指定线程池可支持的最大线程数(最大数量要大于等于核心线程数量) long keepAliveTime, // 指定临时线程的最大存活时间(不能小于0) TimeUnit unit, // 指定存活时间的单位(秒、分、时、天) BlockingQueue<Runnable> workQueue, // 指定任务队列(不能为null) ThreadFactory threadFactory, // 指定用哪个线程工厂创建线程(不能为null) RejectExecutorHandler handler) // (拒绝策略)任务满的时候如何处理(不能为null) Extends:关于新任务拒绝策略 ThreadPoolExecutor.AbortPolicy; // 丢弃任务并抛出异常(RejectExecutorException)(默认策略) ThreadPoolExecutor.DiscardPolicy; // 丢弃任务,但是不抛出异常,这是不推荐的做法 ThreadPoolExecutor.DiscardOldestPolicy; // 抛弃队列中等待最久的任务,然后把当前任务加入道队列当中 ThreadPoolExecutor.CallerRunsPolicy; // 由主线程负责调用任务的run()方法从而绕过线程池直接执行 */ public static void main(String[] args) { // 1. 创建ThreadPoolExecutor线程池 ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( 3, // 核心线程数量 10, // 最大线程数量(核心线程+临时线程) 100, // 最大存活时间数 TimeUnit.SECONDS, // 存活时间单位 new ArrayBlockingQueue<>(6), // 任务队列大小 Executors.defaultThreadFactory(), // 指定线程工厂,用作操作线程 new ThreadPoolExecutor.AbortPolicy() // 拒绝策略 ); // 2. 创建任务对象 TimeRunnable timeRunnable = new TimeRunnable(); // 3. 提交任务, 先有核心线程负责完成这几个任务 threadPoolExecutor.submit(timeRunnable); threadPoolExecutor.submit(timeRunnable); threadPoolExecutor.submit(timeRunnable); // 核心线程处于正在处理状态,即将任务放到任务队列当中,等待核心线程处理结束后继续交给核心线程 threadPoolExecutor.submit(timeRunnable); threadPoolExecutor.submit(timeRunnable); threadPoolExecutor.submit(timeRunnable); threadPoolExecutor.submit(timeRunnable); threadPoolExecutor.submit(timeRunnable); threadPoolExecutor.submit(timeRunnable); // 此时核心线程处于正在处理状态,任务队列也满了,所以此时交给临时任务去处理 threadPoolExecutor.submit(timeRunnable); threadPoolExecutor.submit(timeRunnable); threadPoolExecutor.submit(timeRunnable); threadPoolExecutor.submit(timeRunnable); threadPoolExecutor.submit(timeRunnable); threadPoolExecutor.submit(timeRunnable); threadPoolExecutor.submit(timeRunnable); // 此时核心线程处于正在处理状态,任务队列也满了,并且临时任务也都在正在处理状态,此时去增加处理的任务,就会采用拒绝策略 //threadPoolExecutor.submit(timeRunnable); } } class TimeRunnable implements Runnable { @Override public void run() { System.out.println(Thread.currentThread().getName() + "正在执行任务!"); try { Thread.sleep(1000 * 60); } catch (InterruptedException e) { e.printStackTrace(); } } }
3、Lambda表达式
3.1、概述
- Lambda表达式是JDK8开始后的一种新语法形式
3.2、作用
- 简化匿名内部类的代码写法
3.3、格式
-
() -> { 方法体 }
3.4、有参格式
-
(形式参数) -> { 代码块 }
3.5、注意
- 方法的参数必须是接口,并且接口中只能由一个参数方法
- 接口中只能有一个抽象方法
3.6、省略规则
- 参数类型可以省略(Integer o1, Integer o2),省略后(o1, o2)
- 如果参数有且仅有一个,那么小括号可以省略(String s),省略后 s
- 如果代码块的语句只有一条,可以省略{};和return
4、Stream流
4.1、Stream流介绍
- 作用
- 简化集合和数组的操作
- 操作步骤
- 获取Stream流
- 中间方法
- 终结方法
- 思想
- Stream流相当于工厂里面的流水线,对数据进行加工处理
4.2、Stream流的获取
- 集合获取Stream流的方式
- default Stream
stream() // 获取当前集合对象的Stream流
- default Stream
- 数组获取Stream流的方式
- public static
Stream of(T... values) // 获取当前数组/可变数据的Stream流
- public static
4.3、常用的流操作
4.3.1、中间方法
- eg
- filter // 过滤
- limit // 取前n个
- skip // 跳过前n个
- distinct // 去除重复
- concat // 两个流合成一个流
- map // 转换流中数据的类型
- 注意事项
- 中间方法也称非终结方法,调用完成后返回新的Stream流可以继续使用
- 支持链式编程。在Stream流中无法直接修改集合、数组等数据源中的数据
4.3.2、常见终结操作方法
- forEach // 遍历流中的数据
- count // 元素数据
- 注意事项
- 终结操作方法,调用完成后流就无法继续使用了,原因是不会返回Stream流了
4.4、Stream流的收集操作
- Stream流的收集方法
- R collection (Collector collector) // 开始收集Stream流,指定收集源)
- Object[] toArray() // 把元素收集到数组中
- 工具类Collectors提供了具体的收集方式
- public static
Collector toList() // 把元素收集到List集合中 - public static
Collector toSet() // 把元素收集到Set集合中
- public static