目录
一、基础概念
2.CompletableFuture
3.实战案例
1.线程基础知识复习
1)、1把锁:synchronized
2)、2个并:
并发(concurrent): 是指在同一实体上的多个事件,在一台处理器上“同时处理多个任务”,同一时刻其实只有一个事件发生【一对多】
并行(parallel):是在不同实体上的多个事件,多台处理器上同时处理多个任务,同一时刻各自互不干涉【多对多】
3)、3个程:
进程:系统分配资源的基本单位;
线程:操作系统调度资源的基本单位;
管程:Monitor(监视器),也就是我们平时所说的锁; Monitor其实是一种同步机制,他的义务是保证同一时间只有一个线程可以访问被保护的数据和代码;
例如:
Object o = New Object();
synchronized(o){
} //其中的这个o就属于一个监视器(Monitor),谁持有这个对象o,谁就有了线程的锁,可以进入线程,别的线程只能等待;
【注:】JVM中同步是基于进入和退出监视器对象(Monitor,管程对象)来实现的,
4)、用户进程和守护进程:
Java线程分为用户线程和守护线程,默认都是用户线程。
(user thread)用户线程:是系统的工作进程,他会完成这个程序所需要完成的业务操作;
(daemo thread)守护线程:它是一种特殊的线程,是为其他线程服务的,在后台一直默默地完成一些系统性的服务,比如垃圾回收线程就是最经典的例子;
【守护线程作为一个服务线程,没有服务对象就没必要继续运行了。如果用户线程全部结束了,意味着程序需要完成的业务操作已经结束了,系统在这个时候就可以退出了。
所以假如当系统只剩下守护线程的时候,Java虚拟机会自动退出。】
(isDaemo()方法)线程的daemo属性:True表示是守护线程,False表示是用户线程。
code:
public static void main(String[] args) {
Thread t1 = new Thread(()->{
System.out.println(Thread.currentThread().getName()+"\t开始运行,"+
(Thread.currentThread().isDaemon()?"守护进程":"用户进程"));
while(true){
}
},"t1");
//让t1成为守护进程
t1.setDaemon(true);
t1.start();
//3秒之后,主线程结束(不存在),守护进程也退出,程序结束
try {
TimeUnit.SECONDS.sleep(3);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"---end 主线程");
}
总结: 如果用户进程全部结束意味着程序需要完成的业务操作已经结束了,守护线程随着JVM一同结束工作;
setDaemo()方法必须再start()方法之前进行设置,否则会报错!
1)、Future接口理论知识复习:
Future接口(FutureTask实现类)定义了一些操作异步任务的方法,比如获取异步任务的执行结果,取消任务的执行,判断任务
是否被取消,判断任务是否完毕等等;
【Future接口可以为主线程开一个分支任务,专门为主线程处理耗时和费力的复杂业务】
2)、异步多线程任务执行且返回结果,三个特点:
多线程,有返回,异步任务.(班长为老师取买水作为新启动的异步线程任务且买到水有结果返回 )
所以FutureTask类就应运而生了,它既实现了RunnableFuture接口,满足了多线程和异步,又在构造器中引入了Callable接口,所以说
满足了异步多线程的条件。
coding:
public class FutureThreadPoolDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
m1();
}
public static void m1() throws ExecutionException, InterruptedException {
//3个任务,目前只有一个线程main来处理,耗时多少?
ExecutorService threadPool = Executors.newFixedThreadPool(3);
long startTime=System.currentTimeMillis();
FutureTask futureTask1 = new FutureTask(()->{
try {
TimeUnit.MILLISECONDS.sleep(500);
}catch (InterruptedException e){
e.printStackTrace();
};
return "task1 over";
});
threadPool.submit(futureTask1);
FutureTask futureTask2 = new FutureTask(()->{
try {
TimeUnit.MILLISECONDS.sleep(300);
}catch (InterruptedException e){
e.printStackTrace();
};
return "task2 over";
});
threadPool.submit(futureTask2);
System.out.println(futureTask1.get());
System.out.println(futureTask2.get());
try{
//相当于Thread.sleep(300);
TimeUnit.MILLISECONDS.sleep(300);
}catch (InterruptedException e){
e.printStackTrace();
}
long endTime = System.currentTimeMillis();
System.out.println("---costTime:"+(endTime-startTime)+"毫秒");
System.out.println(Thread.currentThread().getName()+"线程..\t end");
}
}
3)、Future优缺点分析:
优点:Future+线程池异步多线程任务配合,能显著的提高程序的执行效率。
缺点:①get()阻塞:一旦调用get()方法,不管是否计算完成,都会导致程序阻塞,所以get()方法的位置一般放在程序最后
②isDone()轮询:轮询的方式会耗费无谓的CPU资源,而且也不见得能及时地得到计算结果,如果想要异步获取结果,通常会以
轮询地方式去获取结果,尽量不要阻塞;
结论:Future对于结果的获取不是很友好。
4)、CompletableFuture 对Future的改进
·在Java8中,CompletaFuture提供了非常强大的Future扩展功能,可以帮助我们简化异步编程的复杂性,并且提供了函数式编程的能力,
可以通过回调的方式处理计算结果,也提供了转换和组合CompletableFuture的方法。
·它可能代表一个明确完成的Future,也有可能代表一个完成阶段(CompletionStage),它支持在计算完成以后触发一些函数或执行某些动作。
·它实现了Future接口和CompletionStage接口。
5)、异步多任务处理
·利用核心的四个静态方法创建一个异步操作 ,不建议直接new
·关键就是有没有返回值,是否用了线程池
参数说明:
·没有指定Executor的方法,直接使用默认的ForkJoinPool.commPool()作为它的线程池执行异步代码
·如果指定线程池,则使用我们定义的或者特别指定的线程池执行异步代码
①runAsync()无返回值
public static CompletableFuture<Void> runAsync(Runnable runnable) {
return asyncRunStage(asyncPool, runnable);
}
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor) {
return asyncRunStage(screenExecutor(executor), runnable);
}
②supplyAsync()有返回值
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) {
return asyncSupplyStage(asyncPool, supplier);
}
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,Executor executor) {
return asyncSupplyStage(screenExecutor(executor), supplier);
}
【上述Executor executor参数说明:】没有指定的Executor的方法,则直接使用默认的ForkJoinPool.commonPool()作为它的线程池执行异步代码。
如果指定线程池,则使用我们自定义的或者特别指定的线程池执行异步代码。
CompletableFuture优点总结:
6)、Join()和get()方法的区别:
Join()方法和get()方法功能性一样,但是join()在编译时不报异常,get()在编译时报异常
7)、关于链式调用:
只需在类上添加@Accessors(chain=true)即可通过链式调用set/get方法
-比价网站case:
需求说明
1.1 同一款产品,同时搜索出同款产品在各大电商平台的售价;
1.2 同一款产品,同时搜索出本产品在同一个电商平台下,各个入驻卖家售价是多少
输出返回:
出来结果希望是同款产品的在不同地方的价格清单列表, 返回一个List
《mysql》in jd price is 88.05
《mysql》in dang dang price is 86.11
《mysql》in tao bao price is 90.43
解决方案
比对同一个商品在各个平台上的价格,要求获得一个清单列表
1 step by step , 按部就班, 查完京东查淘宝, 查完淘宝查天猫......
2 all in ,万箭齐发,一口气多线程异步多任务操作
coding:
public class CompletableFutureMallDemo {
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
List list1 = getPrice(list, "mysql");
for (String s : list1) {
System.out.println(s);
}
long endTime = System.currentTimeMillis();
System.out.println("-----costTime:" + (endTime - startTime) + "毫秒");
System.out.println("*****************************");
long startTime1 = System.currentTimeMillis();
List list2 = getPriceByCompletableFuture(list, "mysql");
for (String s : list2) {
System.out.println(s);
}
long endTime1 = System.currentTimeMillis();
System.out.println("-----costTime:" + (endTime1 - startTime1) + "毫秒");
}
static List list = Arrays.asList(
new NetMall("jd"),
new NetMall("tb"),
new NetMall("dd")
);
/**
* 一家家搜
*
* @param list
* @param produceName
* @return
*/
public static List getPrice(List list, String produceName) {
return list.stream().map(netMall -> String.format(produceName + " in %s price is %.2f",
netMall.getNetMallName(), netMall.calcPrice(produceName)
)).collect(Collectors.toList());
}
public static List getPriceByCompletableFuture(List list, String produceName) {
//起多个线程将netMall的数据全部映射到CompletableFuture里面
return list.stream().map(netMall -> CompletableFuture.supplyAsync(() ->
String.format(produceName + " in %s price is %.2f",
netMall.getNetMallName(), netMall.calcPrice(produceName)
))).collect(Collectors.toList()
).stream().map(s -> s.join())
.collect(Collectors.toList());
}
}
class NetMall {
@Getter
private String netMallName;
public NetMall(String netMallName) {
this.netMallName = netMallName;
}
public double calcPrice(String productName) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
return ThreadLocalRandom.current().nextDouble() * 2 + productName.charAt(0);
}
}