参考:https://blog.csdn.net/weixin_50330544/article/details/131687150
1.线程池
为什么使用线程池?
频繁申请/销毁资源和调度资源,将带来额外的消耗,可能会非常巨大。
对资源无限申请缺少抑制手段,易引发系统资源耗尽的风险。
系统无法合理管理内部的资源分布,会降低系统的稳定性。
使用线程池的好处?
重用存在的线程,减少对象创建、消亡的开销。
有效的控制最大并发数,提高系统资源使用率。
统一的分配、调优和监控、可定时执行、定期执行。
线程池所在包java.util.concurrent
顶级接口Executor,真正的线程池接口是ExecutorService
Executors类提供创建线程池的方法
Java内置的线程池
自定义线程池
当线程池满了之后就会执行拒绝策略。线程池数量等于最大线程数+阻塞队列数
1、AbortPolicy
当任务添加到线程池中被拒绝时,它将抛出RejectedExecutionException异常。(该策路下,直接丢弃任务,并抛出RejectedExecutionException异常)
2、DiscardPolicy
当任务添加到线程池中被拒绝时,默认情况下它将丢弃被拒绝的任务。(即该策略下,直接丢弃任务,什么都不做)
3.DiscardoldestPolicy
当任务添加到线程池中被拒绝时,线程池会放弃等待队列中最旧的未处理任务,然后将被拒绝的任务添加到等待队列中。(该策略下,抛弃进入队列最早的那个任务,然后尝试把这次拒绝的任务放入队列)
4、CallerRunsPolicy
不进入线程池执行,在这种方式(CallerRunsPolicy)中,任务将由调用者线程去执行。(用于被拒绝任务的处理程序,它直接在execute方法的调用线程中运行被拒绝的任务:如果执行程序已关闭,则会丢弃该任务。)
2.Future和CompletableFuture的用法
在Java多线程编程中,Future是一个接口,用于表示一个异步计算的结果。当启动callable线程时,就可以声明一个Future,用于接收返回结果。它提供了一系列的方法,用于管理和获取任务的执行结果。Future接口定义在java.util.concurrent包中。
使用Future可以将任务提交给线程池执行,并在需要时获取任务的执行结果。它的主要作用是允许主线程在提交异步任务后,继续执行其他操作,而不需要等待任务执行完成。
下面是Future接口的一些常用方法:
1、boolean cancel(boolean mayInterruptIfRunning): 取消任务的执行。如果任务正在执行,并且2、mayInterruptIfRunning参数设置为true,则会尝试中断任务的执行。
3、boolean isCancelled(): 判断任务是否已被取消。
4、boolean isDone(): 判断任务是否已经完成。
5、V get() throws InterruptedException, ExecutionException: 获取任务的执行结果。如果任务还未执行完毕,get()方法会阻塞直到任务执行完成。如果任务执行过程中发生异常,get()方法会抛出ExecutionException,可以通过getCause()方法获取具体的异常信息。
6、V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException: 在指定的时间内获取任务的执行结果,如果任务在指定时间内未执行完毕,会抛出TimeoutException异常。
通过Future接口,我们可以方便地提交任务并获取任务执行结果。这对于需要处理耗时操作的应用程序非常有用,可以提高系统的并发性和响应性。
栗子:四个刚需(线程)去买房摇号,future获取摇号结果。摇号结果未出,就一直阻塞。
public class FutureTest {
/**
* 买房摇号
*/
public static class Yaohao implements Callable<Integer> {
/**
* 返回摇号结果
* @return 0:中签 1:没中
* @throws Exception
*/
@Override
public Integer call() throws Exception {
Random random = new Random();
//模拟摇号,10天内出结果
TimeUnit.SECONDS.sleep(random.nextInt(10));
int result = random.nextInt(2);
System.out.println(" "+Thread.currentThread().getName()+" is done!");
return result;
}
}
public static void main(String[] args) throws InterruptedException, ExecutionException {
Yaohao gangxu1 = new Yaohao();
Yaohao gangxu2 = new Yaohao();
Yaohao gangxu3 = new Yaohao();
Yaohao gangxu4 = new Yaohao();
ExecutorService es = Executors.newCachedThreadPool();
Future<Integer> result1 = es.submit(gangxu1);
Future<Integer> result2 = es.submit(gangxu2);
Future<Integer> result3 = es.submit(gangxu3);
Future<Integer> result4 = es.submit(gangxu4);
es.shutdown();
System.out.println("刚需1,摇号结果:"+(result1.get()==1?"中签":"没中"));
System.out.println("刚需2,摇号结果:"+(result2.get()==1?"中签":"没中"));
System.out.println("刚需3,摇号结果:"+(result3.get()==1?"中签":"没中"));
System.out.println("刚需4,摇号结果:"+(result4.get()==1?"中签":"没中"));
}
}
两个例子
public class FutureTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(10);
UserInfoService userInfoService = new UserInfoService();
MedalService medalService = new MedalService();
long userId =666L;
long startTime = System.currentTimeMillis();
//调用用户服务获取用户基本信息
FutureTask<UserInfo> userInfoFutureTask = new FutureTask<>(new Callable<UserInfo>() {
@Override
public UserInfo call() throws Exception {
return userInfoService.getUserInfo(userId);
}
});
executorService.submit(userInfoFutureTask);
Thread.sleep(300); //模拟主线程其它操作耗时
FutureTask<MedalInfo> medalInfoFutureTask = new FutureTask<>(new Callable<MedalInfo>() {
@Override
public MedalInfo call() throws Exception {
return medalService.getMedalInfo(userId);
}
});
executorService.submit(medalInfoFutureTask);
UserInfo userInfo = userInfoFutureTask.get();//获取个人信息结果
MedalInfo medalInfo = medalInfoFutureTask.get();//获取勋章信息结果
System.out.println("总共用时" + (System.currentTimeMillis() - startTime) + "ms");
}
}
FutureTask和Future的区别
1、功能不同:Future是一个接口,用于表示一个异步计算的结果,可以用来取消任务、查询结果是否完成以及获取计算结果。FutureTask是一个实现了Future接口的具体类,同时也是一个可执行的任务,可以被Executor执行。
2、使用方式不同:Future通常与ExecutorService一起使用,用来提交任务并获取任务的执行结果。FutureTask既可以直接使用FutureTask对象来提交任务,也可以将其作为Runnable或Callable对象提交给ExecutorService。
3、创建方式不同:创建Future对象时,通常使用ExecutorService提交任务后返回的Future实例。而创建FutureTask对象时,可以直接通过new FutureTask(callable)构造函数或new FutureTask(runnable, result)构造函数来实例化。
4、取消任务的能力不同:Future接口提供了cancel(boolean mayInterruptIfRunning)方法,用于取消任务的执行。而FutureTask类还提供了cancel(boolean mayInterruptIfRunning)方法以及boolean cancel(boolean mayInterruptIfRunning)方法,用于取消任务的执行并返回取消成功与否的信息。
总的来说,Future是一个接口,用于表示一个异步计算的结果;FutureTask是Future接口的具体实现类,同时也是一个可执行的任务。FutureTask相比Future更为灵活,能够直接作为任务提交给ExecutorService执行,并提供了更多的取消任务的方法。
ExecutorService 的submit方法的作用
ExecutorService 是 Java 提供的一个用于管理线程池的接口。它可以用来执行异步任务并管理线程的生命周期。submit 方法是 ExecutorService 接口定义的一个方法,用于向线程池提交一个任务并获取一个 Future 对象来表示任务的执行结果。
具体来说,submit 方法用于提交一个 Callable 或 Runnable 对象到线程池中执行。Callable 是带有返回结果的任务,而 Runnable 是没有返回结果的任务。submit 方法将任务提交给线程池后会立即返回一个 Future 对象,通过这个对象可以获得任务的执行结果或者取消任务的执行。
使用 submit 方法可以方便地管理线程池中的任务,可以通过 Future 对象获取任务的状态、结果或者取消任务的执行。通常情况下,我们可以使用 submit 方法来替代 execute 方法,因为它具有更多的功能和灵活性。
submit 和execute 的区别
submit() 和 execute() 是 ExecutorService 接口用于提交任务到线程池的两种方法,它们之间有以下区别:
1、返回值类型不同: submit() 方法返回一个 Future 对象,可以用来获取任务的执行结果或者取消任务的执行;而 execute() 方法没有返回值,无法获取任务的执行结果。
2、异常处理不同: submit() 方法能够捕获任务执行过程中抛出的异常,将异常封装到 Future 对象中,通过调用 get() 方法或者 get(long, TimeUnit) 方法获取任务的执行结果时,如果任务抛出了异常,可以通过 ExecutionException 来获取异常信息;而 execute() 方法无法对任务抛出的异常进行处理,如果任务抛出了异常,线程池会将异常记录到控制台。
3、参数类型不同: submit() 方法接受 Callable 或者 Runnable 对象作为参数,Callable 是带有返回值的任务,而 Runnable 是没有返回值的任务;而 execute() 方法只接受 Runnable 对象作为参数。因此,如果需要获取任务执行的结果,应该使用 submit() 方法,如果不需要获取结果,可以使用 execute() 方法。
总的来说,submit() 方法比 execute() 方法更加灵活,可以获取任务的执行结果和捕获任务中抛出的异常,因此在处理需要获取结果或者处理异常的任务时,推荐使用 submit() 方法。而对于不需要获取结果的简单任务,可以使用 execute() 方法。
Future的局限
Future对于结果的获取,不是很友好,只能通过阻塞或者轮询的方式得到任务的结果。
Future.get() 就是阻塞调用,在线程获取结果之前get方法会一直阻塞。Future提供了一个isDone方法,可以在程序中轮询这个方法查询执行结果。
阻塞的方式和异步编程的设计理念相违背,而轮询的方式会耗费无谓的CPU资源。因此,JDK8设计出CompletableFuture,提供了一种观察者模式类似的机制,可以让任务执行完成后通知监听的一方。
CompletableFuture 使用详解(重要)
参考:https://www.jianshu.com/p/6bac52527ca4
https://blog.csdn.net/sermonlizhi/article/details/123356877
https://juejin.cn/post/6970558076642394142
CompletableFuture提供了多种方法来获取异步计算的结果。以下是一些常用的方法:
使用get()方法:CompletableFuture类继承了Future接口,因此可以使用get()方法来获取计算的结果。但需要注意的是,get()方法是阻塞的,会等待异步计算完成后返回结果。如果计算没有完成,get()方法会一直阻塞,直到计算完成并返回结果或者抛出异常。
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello");
String result = future.get(); // 阻塞获取计算结果
使用join()方法:与get()方法类似,join()方法也可以获取计算结果,但它是非阻塞的。如果计算没有完成,join()方法会等待计算完成后立即返回结果。
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello");
String result = future.join(); // 非阻塞获取计算结果
使用CompletableFuture组合方法:CompletableFuture提供了一系列方法,如thenApply、thenAccept、thenCompose等,用于对计算结果进行处理。这些方法返回的是新的CompletableFuture实例,可以继续链式调用。通过这些方法,我们可以在异步计算完成后,对结果进行进一步的操作或处理。
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello")
.thenApply(s -> s + " World")
.thenApply(String::toUpperCase);
String result = future.get();
使用回调方法:CompletableFuture还提供了一系列回调方法,如whenComplete、handle、thenAccept、exceptionally等,用于在计算完成后执行相应的操作或处理。这些方法允许我们以非阻塞的方式处理计算结果,并在计算完成或出现异常时执行相应的逻辑。
CompletableFuture.supplyAsync(() -> "Hello")
.thenApply(s -> s + " World")
.thenAccept(System.out::println)
.exceptionally(ex -> {
System.out.println("Error: " + ex.getMessage());
return null;
});
标签:Java,get,submit,任务,Future,线程,方法
From: https://www.cnblogs.com/cgy1995/p/18244490