复习
1.初始化线程的4种方式
1)、继承Thread
2)、实现 Runnable接口
3)、实现 Callable接口+FutureTask(可以拿到返回结果,可以处理异常)
4)、线程池
区别:
1、2不能得到返回值。3可以获取返回值
1、2、3都不能控制资源(无法控制线程数【高并发时线程数耗尽资源】)
4可以控制资源,性能稳定,不会一下子所有线程一起运行
结论:
实际开发中,只用线程池【高并发状态开启了n个线程,会耗尽资源】
public class ThreadTest {
public static ExecutorService threadPool = Executors.newFixedThreadPool(10);
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println("主线程开始");
threadPool.execute(new RunnableTest01());
Future submit = threadPool.submit(new CallableTest01());
Integer integer = (Integer) submit.get();
System.out.println("执行结果为"+integer);
System.out.println("主线程结束");
}
public static class TreadTest01 extends Thread {
@Override
public void run() {
System.out.println("当前线程Id "+Thread.currentThread().getId()+"当前线程名字 "+Thread.currentThread().getName());
int i = 100/20;
System.out.println("运行结果 "+i);
}
}
public static class RunnableTest01 implements Runnable {
@Override
public void run() {
System.out.println("当前线程Id "+Thread.currentThread().getId()+"当前线程名字 "+Thread.currentThread().getName());
int i = 100/20;
System.out.println("运行结果 "+i);
}
}
public static class CallableTest01 implements Callable{
@Override
public Integer call() throws Exception {
System.out.println("当前线程Id "+Thread.currentThread().getId()+"当前线程名字 "+Thread.currentThread().getName());
Integer i = 100/20;
System.out.println("运行结果 "+i);
return i;
}
}
}
2.创建线程池的方式
创建固定线程数的线程池ExecutorService
固定线程数的线程池
Executors.newFixedThreadPool(10);
execute和submit区别
作用:都是提交异步任务的
execute:只能提交Runnable任务,没有返回值
submit:可以提交Runnable、Callable,返回值是FutureTask
创建原生线程池ThreadPoolExecutor
new ThreadPoolExecutor(5,
200,
10,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(100000),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
7个参数:
corePoolSize: 核心线程数,不会被回收,接收异步任务时才会创建
maximumPoolSize:最大线程数量,控制资源
keepAliveime: maximumPoolSize-corePoolSize 无任务存活超过空闲时间则线程被释放
TimeUnitunit: 时间单位
workQueue: 阻塞队列,如果任务有很多,大于最大线程数的其余线程会放到队列里面,只要有线程池里面有空闲线程,就会从队列取出放到线程池执行任务执行
threadFactory: 线程的创建工厂【可以自定义】
RejectedExecutionHandler handler:(执行队列满了过后怎么办)队列满后执行的拒绝策略
线程池任务执行流程
当线程池小于corePoolSize时,新提交任务将创建一个新线程执行任务,即使此时线程池中存在空闲线程。
当线程池达到corePoolSize时,新提交任务将被放入workQueue中,等待线程池中任务调度执行
当workQueue已满,且maximumPoolSize>corePoolSize时,新提交任务会创建新线程执行任务
当提交任务数超过maximumPoolSize时,新提交任务由RejectedExecutionHandler处理(默认策略抛出异常)
当线程池中超过corePoolSize线程,空闲时间达到keepAliveTime时,释放空闲线程
当设置allowCoreThreadTimeOut(true)时,该参数默认false,线程池中corePoolSize线程空闲时间达到keepAliveTime也将关闭
工作顺序:
1)、线程池创建,准备好core数量的核心线程,准备接受任务
1.1 core满了,就将再进来的任务放入阻塞队列中。空闲的core就会自己去阻塞队列获取任务执行
1.2、阻塞队列满了,就直接开新线程执行,最大只能开到max指定的数量
1.3、max满了就用RejectedExecutionHandLer拒绝任务
1.4max都执行完成,有很多空闲.在指定的时间keepAliveTime以后,释放max-core这些线程
new LinkedBLockingDeque<>():默认是Integer的最大值。内存不够
拒绝策略
主要是JUC包下的 查看实现类用ctrl+h
DiscardOldestPolicy:丢弃最老的任务 阻塞队列队头
AbortPolicy:丢弃当前任务,抛出异常【默认策略】
CallerRunsPolicy:同步执行run方法,就是当前线程的run方法执行业务
DiscardPolicy:丢弃当前任务,不抛出异常
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/c6f8bdcfc14add4600626750f0621821.png#pic_center)
阻塞队列
1.new LinkedBlockingDeque<>();// 默认大小是Integer.Max会导致内存不足,所以要做压力测试给出适当的队列大小
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/274ab10e30d6740f286874814352e434.png#pic_center)
线程池
1.常见的4种默认线程池
注意:
回收线程 = maximumPoolSize - corePoolSize
可缓冲线程池【CachedThreadPool】:corePoolSize=0, maximumPoolSize=Integer.MAX_VALUE 所有都可以回收
定长线程池【FixedThreadPool】:corePoolSize=maximumPoolSize 都不可回收
周期线程池【ScheduledThreadPool】:指定核心线程数,maximumPoolSize=Integer.MAX_VALUE,支持定时及周期性任务执行(一段时间之后再执行)
单任务线程池【SingleThreadPool】:corePoolSize=maximumPoolSize=1,从队列中获取任务(一个核心线程)一个一个执行
Executors.newCachedThreadPool();
Executors.newFixedThreadPool(10);
Executors.newScheduledThreadPool(10);
Executors.newSingleThreadExecutor();
2.为什么使用线程池?
1.降低资源的消耗【减少创建销毁操作】
通过重复利用已经创建好的线程降低线程的创建和销毁带来的损耗
高并发状态下过多创建线程可能将资源耗尽
2.提高响应速度【控制线程个数】
因为线程池中的线程数没有超过线程池的最大上限时,有的线程处于等待分配任务的状态,当任务来时无需创建新的线程就能执行(线程个数过多导致CPU调度慢)
3、提高线程的可管理性【例如系统中可以创建两个线程池,核心线程池、非核心线程池【例如发送短信】,显存告警时关闭非核心线程池释放内存资源】
线程池会根据当前系统特点对池内的线程进行优化处理,减少创建和销毁线程带来的系统开销。无限的创建和销毁线程不仅消耗系统资源,还降低系统的稳定性,使用线程池进行统一分配
异步编排CompletableFuture
1.runXXX都是没有返回结果的,supplyXXX可以获取返回结果
2.可以传入自定义线程池,否则使用默认线程池
1.业务场景
4、5、6依赖1,得先知道sku是哪个spu下的
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/b9b64f1022efc533d1acbeb39577248a.png#pic_center)
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/66716d2ccc641834c86d39da6a5c0ef1.png#pic_center)
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/375b0e14d1e9d6195a8a8cef88c1de5a.png#pic_center)
2.测试异步操作
supplyAsync
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "测试使用", executor);
System.out.println(future1.get());
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/737364304a12505cfbada61576457850.png#pic_center)
thenRunAsync串行化
thenAcceptAsync串行化
thenApplyAsync串行化
CompletableFuture<String> future2 = future1.thenApplyAsync(s -> s + " 链式调用", executor);
System.out.println(future2.get());
whenCompleteAsync
CompletableFuture<String> future3 = future2.whenCompleteAsync((result, exception) -> System.out.println("结果是:" + result + "----异常是:" + exception));
exceptionally
CompletableFuture<Integer> future4 = future3.thenApplyAsync((s -> 1 / 0), executor);
CompletableFuture<Integer> future5 = future4.exceptionally(exception -> {
System.out.println("出现异常:" + exception);
return 10;
});
System.out.println("默认值:" + future5.get());
handle
CompletableFuture<Integer> future6 = future3.thenApplyAsync((s -> 1 / 0), executor).handle((result, exception) -> {
if (exception == null) {
return result;
}
System.out.println("handle处理异常:" + exception);
return 1;
});
System.out.println("handle处理返回结果:" + future6.get());
两任务组合-都要完成
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/4d3d2e754c265e2282e58e2d2ffe6761.png#pic_center)
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/cd7448122c32e35d7fe42e5c6cb3017d.png#pic_center)
runAfterBothAsync
CompletableFuture<Integer> future01 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务1执行");
return 10 / 2;
}, executor);
CompletableFuture<String> future02 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务2执行");
return "hello";
}, executor);
CompletableFuture<Void> future03 = future01.runAfterBothAsync(future02, () -> {
System.out.println("任务3执行");
}, executor);
thenAcceptBothAsync
CompletableFuture<Integer> future01 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务1执行");
return 10 / 2;
}, executor);
CompletableFuture<String> future02 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务2执行");
return "hello";
}, executor);
CompletableFuture<Void> future03 = future01.thenAcceptBothAsync(future02,
(result1, result2) -> {
System.out.println("任务3执行");
System.out.println("任务1返回值:" + result1);
System.out.println("任务2返回值:" + result2);
}, executor);
thenCombineAsync
CompletableFuture<Integer> future01 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务1执行");
return 10 / 2;
}, executor);
CompletableFuture<String> future02 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务2执行");
return "hello";
}, executor);
CompletableFuture<String> future03 = future01.thenCombineAsync(future02,
(result1, result2) -> {
System.out.println("任务3执行");
System.out.println("任务1返回值:" + result1);
System.out.println("任务2返回值:" + result2);
return "任务3返回值";
}, executor);
System.out.println(future03.get());
两任务组合-任一完成
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/d56fb66564fb334051b46354a8312059.png#pic_center)
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/02bf544882ce356fdba25134e33ed566.png#pic_center)
runAfterEitherAsync
acceptEitherAsync
applyToEitherAsync
多任务组合
allOf
CompletableFuture<Void> allOf = CompletableFuture.allOf(future01, future02, future03);
allOf.get();
anyOf
CompletableFuture<Object> anyOf = CompletableFuture.anyOf(future01, future02, future03);
anyOf.get();
项目整合异步编排
1.注入线程池
gulimall:
thread:
core-size: 20
max-size: 200
keep-alive-time: 10
@ConfigurationProperties(prefix = "gulimall.thread")
@Data
public class ThreadPoolConfigProperties {
private Integer coreSize;
private Integer maxSize;
private Integer keepAliveTime;
}
@EnableConfigurationProperties(ThreadPoolConfigProperties.class)
@Configuration
public class MyThreadConfig {
@Bean
public ThreadPoolExecutor threadPoolExecutor(ThreadPoolConfigProperties pool) {
return new ThreadPoolExecutor(
pool.getCoreSize(),
pool.getMaxSize(),
pool.getKeepAliveTime(),
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(100000),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()
);
}
}
2.实际业务使用异步编排
@Service("skuInfoService")
public class SkuInfoServiceImpl extends ServiceImpl<SkuInfoDao, SkuInfoEntity> implements SkuInfoService {
@Autowired
SkuImagesService skuImagesService;
@Autowired
SkuSaleAttrValueService skuSaleAttrValueService;
@Autowired
CouponAgentService couponAgentService;
@Autowired
SpuInfoDescService spuInfoDescService;
@Autowired
AttrGroupServiceImpl attrGroupService;
@Autowired
ThreadPoolExecutor executor;
@Override
public SkuItemVO item(Long skuId) throws ExecutionException, InterruptedException {
SkuItemVO result = new SkuItemVO();
CompletableFuture<SkuInfoEntity> skuInfoFuture = CompletableFuture.supplyAsync(() -> {
SkuInfoEntity skuInfo = getById(skuId);
result.setInfo(skuInfo);
return skuInfo;
}, executor);
CompletableFuture<Void> imagesFuture = CompletableFuture.runAsync(() -> {
List<SkuImagesEntity> images = skuImagesService.getImagesBySkuId(skuId);
result.setImages(images);
}, executor);
CompletableFuture<Void> saleAttrFuture = skuInfoFuture.thenAcceptAsync((skuInfo) -> {
List<SkuItemSaleAttrVO> saleAttr = skuSaleAttrValueService.getSaleAttrBySpuId(skuInfo.getSpuId());
result.setSaleAttr(saleAttr);
}, executor);
CompletableFuture<Void> descFuture = skuInfoFuture.thenAcceptAsync((skuInfo) -> {
SpuInfoDescEntity desc = spuInfoDescService.getById(skuInfo.getSpuId());
result.setDesc(desc);
}, executor);
CompletableFuture<Void> groupAttrsFuture = skuInfoFuture.thenAcceptAsync((skuInfo) -> {
List<SpuItemAttrGroupVO> groupAttrs = attrGroupService.getAttrGroupWithAttrsBySpuId(skuInfo.getSpuId(), skuInfo.getCatalogId());
result.setGroupAttrs(groupAttrs);
}, executor);
CompletableFuture.allOf(imagesFuture, saleAttrFuture, descFuture, groupAttrsFuture).get();
return result;
}
}