文章目录
1. java多线程相关概念
1. start一个线程
public class Main {
public static void main(String[] args) {
Thread t1 = new Thread(()->{},"t1");
t1.start(); // 调用的底层方法 private native void start0();
}
}
2. 锁
锁:synchronized
3. 并
俩个并:并发(concurrent,JUC的C)、并行(parallel)
- 并发:在一个实体上的多个事件,在同一台处理器上“同时”处理多个任务。同一时刻实际上只有一个事件在发生。
- 并行:在多个实体上的多个事件,在多个处理器上同时处理多个任务。同一时刻有多个事件同时发生,相互不干扰。
4. 程
三个程:进程、线程、管程
- 进程:在系统中每一个应用程序就是一个进程,每个进程都有自己的内存空间和资源。
- 线程:也称为轻量级进程,一个进程中有1个或多个线程,是大多数操作系统进行时序调度的基本单元。
- 管程:Monitor(监视器),也就是锁。Monitor是一种同步机制,他的义务是保证(同一时间)只有一个线程可以访问被保护的数据和代码。JVM中同步是基于进入和退出监视器对象来实现的,每个对象实例都会有一个Monitor。Monitor对象会和JAVA对象一同创建和销毁。
Object o = new Object(); // 一个monitor
new Thread(()->{
synchronized (o) {
}
}, "t2").start(8);
}
5. 用户线程和守护线程
一般情况不做特殊说明配置,默认都是用户线程。
- 用户线程(User Thread):(比如main线程)是系统的工作线程,他会完成这个程序需要完成的业务操作。
- 守护线程(Daemon Thread):
是一种特殊的线程为其他线程服务的 ,在后台默默完成一些系统性的服务(相当于影子,比如GC垃圾回收线程)。
守护线程作为一个服务线程,没有服务对象就没有必要继续运行了。如果用户进程全部结束,就意味着程序需要完成的业务操作已经结束了,系统可以退出了。所以当系统只剩下守护线程时,JAVA虚拟机会自动退出。
public final boolean isDaemon(){
return daemon;
// true是守护线程,false是用户线程。
// 也可以通过setDaemon(boolean)设置
}
public class Main {
public static void main(String[] args) {
Thread t1 = new Thread(()->{
System.out.println(Thread.currentThread().getName()+"\t 开始运行,"+
(Thread.currentThread().isDaemon()? "守护线程": "用户线程"));
},"t1");
// 必须在start之间使用,不然会报错
// 用户线程全部结束后,守护线程随jvm一同结束工作
t1.setDaemon(true);
t1.start(); // private native void start0();
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName()+"\t 主线程");
}
}
2. CompletableFuture
1. Future
Future接口(FutureTask实现类)定义了操作异步任务执行的一些方法,如获取异步任务的执行结果、取消任务的执行、判断任务是否被取消、判断任务是否完毕等。
Future接口可以为主线程开一个分支,专门去处理主线程一些耗力费时的复杂任务。
Future是JAVA5加入的一个接口,提供了异步并行计算的功能。如果主线程需要处理一个很耗时的计算任务,就可以通过future把这个任务放到异步线程中执行。主线程继续处理其他任务或先行结束,在通过future获取计算结果。
实现多线程、有返回、异步任务: FutureTask实现类
new Thread(Runnable)
要传入Runnable
实现了RunnableFuture接口,构造方法实现了Callable
public class FutureDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 传入Callable
FutureTask<String> futureTask = new FutureTask<>(new MyThread());
Thread t1 = new Thread(futureTask);
t1.start();
// futureTask.get() 获取返回值
// System.out.println(futureTask.get());
while (true) {
if(futureTask.isDone()) {
System.out.println(futureTask.get());
break;
} else {
// 给后台一个提示
TimeUnit.MILLISECONDS.sleep(500);
System.out.println("正在处理中......");
}
}
}
}
class MyThread implements Callable{
@Override
public Object call() throws Exception {
System.out.println("----------come in call()");
TimeUnit.SECONDS.sleep(5);
return "hello callable";
}
}
创建线程池
ExecutorService threadPool = Excutors.newFixedThreadPool(3);
1. Future优缺点
- 优点:Future+线程池异步多线程任务配合,可以显著提高程序的执行效率。
- 缺点:
futureTask.get()
get容易堵塞,必须等到结果才离开,不管你计算是否完成,一般放在程序后面,不然容易造成程序堵塞。想要等待指定的时间futureTask.get(3, TimeUnit.SECONDS)
只等3秒,过时没有拿到结果就抛异常。isDone()
轮询给后台一个提示。如果想要异步获取结果,通常都会以轮询的方式去获取结果,尽量不要堵塞。但是轮询的方式会耗费更多的CPU资源,而且不见得可以得到计算结果。- Future对于结果的获取不是很友好,只能通过阻塞或轮询的方式。
想要实现的一些功能…Future无法满足–> 引出CompletableFuture
2. CompletableFuture
CompletableFuture提供了一种观察者类似的模型,可以让任务执行完成后通知监听的一方
- CompletableFuture类:
- CompletableFuture提供了强大的Future的扩展功能,可以帮助我们简化异步编程的复杂性,并且提供了函数式编程的能力,可以通过回调的方式处理计算结果,也提供了转换和组合的CompletableFuture的方法。
- 他可能可能代表一个明确完成的Future,也有可能代表一个完成阶段(CompletionStage),他支持在计算完成以后触发一些函数或执行某些动作。
- 实现了Future和Completable接口。
- CompletionStage接口:
- CompletionStage代表异步计算过程中的某一个阶段,一个阶段完成以后可以能会出发另一个阶段。类似Linux系统的管道分隔符传参数。
- 一个阶段的计算执行可以是一个Function,Consumer或者Runnable。
- 一个阶段的执行可能是被单个阶段的完成触发,也可能是由多个阶段一起触发。
1. 四大核心静态方法
无返回值(默认线程池(ForkJoinPool.commonPool() )、指定线程池)、有返回值(默认线程池(ForkJoinPool)、指定线程池)
public class CompletableFutureDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 建立线程池
ExecutorService threadPool = Executors.newFixedThreadPool(3);
// 传入runnable,无返回值
CompletableFuture<Void> runAsync = CompletableFuture.runAsync(()->{
System.out.println(Thread.currentThread().getName());
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}, threadPool);
System.out.println(runAsync.get());
// System.out.println(runAsync.join());
// 和get的区别,在编译时不会报出检查性异常。
// 传入Callable,有返回值
CompletableFuture<Object> supplyAsync = CompletableFuture.supplyAsync(()->{
System.out.println(Thread.currentThread().getName());
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "hello supplyAsync";
});
System.out.println(supplyAsync.get());
// 关闭线程池
threadPool.shutdown();
}
}
2. 减少阻塞和轮询
减少阻塞和轮询,可以传入回调对象,当异步任务完成或发生异常时,自动调用回调对象的回调方法。
public class CompletableFutureUseDemo {
public static void main(String[] args) {
ExecutorService threadPool = Executors.newFixedThreadPool(3);
try{
CompletableFuture.supplyAsync(()->{
System.out.println(Thread.currentThread().getName());
int result = ThreadLocalRandom.current().nextInt(10);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("一秒钟出结果" + result);
if(result > 2) {
int i = 10/0;
}
return result;
// v就是supplyAsync()的结果,e是异常
}, threadPool).whenComplete((v,e)->{
if(e == null) {
System.out.println("------计算完成,更新值:"+v);
}
}).exceptionally(e->{
e.printStackTrace();
System.out.println("异常情况" +e.getCause()+"\t"+e.getMessage());
return null;
});
System.out.println(Thread.currentThread().getName()+"线程运行其他任务....");
}catch (Exception e){
e.printStackTrace();
}finally {
threadPool.shutdown();
}
// ForkJoinPool类似一个守护线程,主线程结束之后CompletableFuture默认使用的线程池会立刻关闭。
// 不用线程池时
// try {
// TimeUnit.SECONDS.sleep(3);
// } catch (InterruptedException e) {
// throw new RuntimeException(e);
// }
}
}
3. CompletableFuture优点
- 异步任务结束时,会自动回到某个对象的方法;
- 主线程设置好回调后,不再关心异步任务的执行,异步任务之间可以顺序执行;
- 异步任务出错时,会自动回调某个对象的方法。
3. CompletableFuture实战:电商网站比价
1. 基础知识
- Lamda表达式:
- Stream流式调用:
- Chain链式调用:
public class Demo{
public static void main(String[] args) {
Student student = new Student();
// 链式调用
student.setId(22).setAge(11).setName("zs");
}
}
@Accessors(chain=true)
class Student{
}
- JAVA8函数式编程:
接口编程
2. 需求说明
3. 实现
1. 一步一步实现
耗时长(3s)
public class CompletableFutureMallDemo {
static List<NetMall> list = Arrays.asList(
new NetMall("jd"),
new NetMall("pxx"),
new NetMall("taobao")
);
// 一步一步实现
public static List<String> getPrice(List<NetMall> list, String productName) {
// 流式计算
return list.stream()
// 映射
.map(netMall ->
String.format(productName + " in %s price is %.2f",
netMall.getNetMallName(),
netMall.calcPrice(productName)))
// 转化为list
.collect(Collectors.toList());
}
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
List<String> mysql = getPrice(list, "mysql");
for (String l :
mysql) {
System.out.println(l);
}
long endTime = System.currentTimeMillis();
System.out.println("-------constTime"+(endTime-startTime) + "毫秒");
}
}
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) {
throw new RuntimeException(e);
}
return ThreadLocalRandom.current().nextDouble() * 2 + productName.charAt(0);
}
}
2. 使用CompletableFuture实现
快速(1s)
//CompletableFuture实现
public static List<String> getPriceByCompletableFuture(List<NetMall> list, String productName) {
return list.stream()
// 映射
.map(netMall -> CompletableFuture.supplyAsync(() ->
String.format(productName + " in %s price is %.2f",
netMall.getNetMallName(),
netMall.calcPrice(productName))))
.collect(Collectors.toList())
.stream().map(a -> a.join())
.collect(Collectors.toList());
}
4. CompletableFuture常用方法
1. 获得结果和触发计算
getNow(T v)
没有计算完成的情况下给一个替代结果,计算完成返回计算的值
System.out.println(supplyAsync.getNow("备胎值"));
complete(T value)
如果打断了join()方法,返回value的值;如果没有打断,返回计算的值
System.out.println(supplyAsync.complete("备胎值2")+"\t"+supplyAsync.join());
2. 对计算机结果进行处理
ExecutorService threadPool = Executors.newFixedThreadPool(2);
CompletableFuture.supplyAsync(()-> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("111");
return 1;
}, threadPool).thenApply(f->{
System.out.println("222");
return f+2;
}).thenApply(f->{
System.out.println("333");
return f+3;
}).whenComplete((v,e)->{
if(e == null) {
System.out.println("------计算完成:"+v);
}
}).exceptionally(e->{
e.printStackTrace();
System.out.println("异常情况" +e.getCause()+"\t"+e.getMessage());
System.out.println("异常情况" +e.getCause()+"\t"+e.getMessage());
return null;
});
threadPool.shutdown();
handle是传入俩个参数
.handle((f,e)->{
System.out.println("222");
return f+2;
})
3. 对计算机结果进行消费
接收任务处理的结果,并消费处理,无返回结果
1.thenAccept
// thenAccept
CompletableFuture.supplyAsync(()-> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("111");
return 1;
}, threadPool).thenApply(f->{
return f+3;
}).thenAccept(System.out::println);
2. 对比补充
System.out.println(CompletableFuture.supplyAsync(()->"reslutA").thenRun(()->{}).join());
System.out.println("------------");
System.out.println(CompletableFuture.supplyAsync(()->"reslutA").thenAccept(r->{
System.out.println(r);
}).join());
System.out.println("------------");
System.out.println(CompletableFuture.supplyAsync(()->"reslutA").thenApply(f->f+"resiltB").join());
/* 结果
null
------------
reslutA
null
------------
reslutAresiltB
*/
3. CompletableFuture和线程池
- 没有传入自定义线程池,都默认使用ForkJoinPool。
- 传入了一个自定义线程池:
- 调用
thenRun
方法执行第二个任务时,第二个和第一个使用的是同一个线程池。 - 调用
thenRunAsync
方法执行第二个任务时,第一个任务使用的是你自己传入的线程池,第二个使用的是ForkJoinPool。
- 调用
- 如果处理的太快,系统优化切换原则,直接使用main线程处理
源码分析
public CompletableFuture<Void> thenRun(Runnable action) {
return uniRunStage(null, action);
}
public CompletableFuture<Void> thenRunAsync(Runnable action) {
return uniRunStage(asyncPool, action);
}
// 判断是否使用默认线程池
/**
* Default executor -- ForkJoinPool.commonPool() unless it cannot
* support parallelism.
*/
private static final Executor asyncPool = useCommonPool ?
ForkJoinPool.commonPool() : new ThreadPerTaskExecutor();
4. 对计算机速度进行选用
谁快用谁
applyToEither
completableFuture1.applyToEither(completableFuture2, f->{
return f+"is winner";
});
5. 对计算机结果进行合并
- 两个completionStage任务都完成后,最终能把两个任务的结果一起交给thenCombine来处理
- 先完成的先等着,等待其它分支任务
thenCombine
completableFuture1.thenCombine(completableFuture2, (x,y)->{
// 俩个结果合并
return x + y;
});
尚硅谷JUC学习笔记…