实现的代码封装在function中,也有简单的说明,如下:
public static void useCompletableFuture_complicated() {
// 这个方法时描述一般地使用CompletableFuture实现异步操作,即复杂的使用CompletableFuture实现异步操作
// 假设我们有一个Person名字List
List<String> personNameList = new ArrayList<>();
// 为了方便测试,我们要构造大量的数据add到personNameList,用for循环,名字就是1, 2, 3, ...
// 这里添加1000个名字到personNameList
for (int i = 0; i < 1000; i++) {
personNameList.add(String.valueOf(i));
}
// 假设我们要做的业务是personNameList里的每个人都说一句Hello World, 但是我们不关心他们说这句话的顺序,而且我们希望这个业务能够较快速的完成,所以采用异步就是比较合适的
// 先创建两个活动线程的线程池
ExecutorService executor = Executors.newFixedThreadPool(2);
// 开始我们的业务处理
for (String personName : personNameList) {
CompletableFuture.supplyAsync(new Supplier<String>() {
@Override
public String get() {
// 模拟业务逻辑,say hello world
System.out.println(personName + ": Hello World!");
return "task finished!";
}
}, executor);
}
// 关闭线程池executor
// 说明一下executor必须要显示关闭(它的方法里有介绍),不然线程池会一直等待任务,会导致main方法一直运行
// 还有就是关闭executor,不会导致之前提交的异步任务被打断或者取消。即之前提交的任务依然会执行到底,只是不会再接收新的任务
executor.shutdown();
/* 那么关闭线程池之后,我们怎么确定我们的任务是否都完成了呢,可以使用executor.isTerminated()命令
// 可以看看isTerminated这个方法的说明,简单的说就是调用isTerminated()方法之前没有调用shutdown()方法的话,那么isTerminated()方法返回的永远是false。
// 所以isTerminated()方法返回true的情况就是在调用isTerminated()方法之前要先调用shutdown()方法,且所有的任务都完成了。
// 其实调用isTerminated()的目的就是我们对异步任务的结果是care, 我们需要等待异步任务的结果以便我们做下一步的动作。
// 如果我们不关心异步任务的结果的话,完全可以不用调用isTerminated()。
*/
while (!executor.isTerminated()) {
System.out.println("no terminated");
try {
System.out.println("我要休眠一下");
TimeUnit.MILLISECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
简洁的代码如下:
public static void useCompletableFuture_simple() {
// 这个方法时描述利用1.8新特性,简单使用CompletableFuture实现异步操作
// 先创建两个活动线程的线程池
ExecutorService executor = Executors.newFixedThreadPool(2);
List<String> nameList = new ArrayList<String>();
for (int i = 0; i < 1000; i++) {
nameList.add(String.valueOf(i));
}
// 使用JDK 1.8的特性,stream()和Lambda表达式: (参数) -> {表达式}
nameList.stream().forEach(name -> CompletableFuture.supplyAsync((Supplier<String>) () -> {
print((String) name); // 封装了业务逻辑
return "success";
}, executor).exceptionally(e -> {
System.out.println(e);
return "false";
}));
executor.shutdown();
while (!executor.isTerminated()) {
System.out.println("no terminated");
try {
System.out.println("我要休眠一下");
TimeUnit.MILLISECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
比喻:会输出1000个: x: Hello World!
x的范围是0-999,它们之间是没有顺序,中间会有’no terminated’和’我要休眠一下’这两句话。
使用JDK1.8新特性代码简洁了很多,格局看起来有上来了。
CompletableFuture.supplyAsync((Supplier<String>) () -> {
CompletableFuture.supplyAsync(() -> {
日期:2021-8-14
以上两个例子使用的线程池都是默认的,查看源码:
/**
* Creates a thread pool that reuses a fixed number of threads
* operating off a shared unbounded queue. At any point, at most
* {@code nThreads} threads will be active processing tasks.
* If additional tasks are submitted when all threads are active,
* they will wait in the queue until a thread is available.
* If any thread terminates due to a failure during execution
* prior to shutdown, a new one will take its place if needed to
* execute subsequent tasks. The threads in the pool will exist
* until it is explicitly {@link ExecutorService#shutdown shutdown}.
*
* @param nThreads the number of threads in the pool
* @return the newly created thread pool
* @throws IllegalArgumentException if {@code nThreads <= 0}
*/
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
可知newFixedThreadPool用的是LinkedBlockingQueue,即阻塞的链表队列,再通过源码看看这个队列的默认大小。
/**
* Creates a {@code LinkedBlockingQueue} with a capacity of
* {@link Integer#MAX_VALUE}.
*/
public LinkedBlockingQueue() {
this(Integer.MAX_VALUE);
}
默认大小是Integer.MAX_VALUE,2^31次方,2147483648,21亿多,可以认为是无限大。即这个任务队列是无界的。如果这个时候每个任务的执行时间是非常长,又不断的加任务进去,就会因为线程处理不完,导致内存飙升。
那如何修改呢?这个我目前也在寻找答案。有结果定会更新出来。
日期:2022-1-14
目的:解决上面使用默认的线程池,池大小是无界的问题。
调整了两个部分,请看下面:
<1>. executor的调整
原来(AS_IS):
// 先创建两个活动线程的线程池
ExecutorService executor = Executors.newFixedThreadPool(2);
修改(TO_BE):
//使用阿里巴巴推荐的创建线程池的方式
//通过ThreadPoolExecutor构造函数自定义参数创建
ThreadPoolExecutor executor = new ThreadPoolExecutor(
5,
10,
1L,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(100),
new ThreadPoolExecutor.CallerRunsPolicy());
简单说明:通过构造函数自定义线程池参数,参数的意义大家可以自行百度或者查看源码。
<2>. forEach的调整(事实上与这个问题无关)
原来(AS_IS):
// 使用JDK 1.8的特性,stream()和Lambda表达式: (参数) -> {表达式}
nameList.stream().forEach(name -> CompletableFuture.supplyAsync((Supplier<String>) () -> {
修改(TO_BE):
// 使用JDK 1.8的特性,Lambda表达式: (参数) -> {表达式}
nameList.forEach(name -> CompletableFuture.supplyAsync((Supplier<String>) () -> {
总的代码:
public static void useCompletableFuture_simple() {
// 这个方法时描述利用1.8新特性,简单使用CompletableFuture实现异步操作
//使用阿里巴巴推荐的创建线程池的方式
//通过ThreadPoolExecutor构造函数自定义参数创建
ThreadPoolExecutor executor = new ThreadPoolExecutor(
5,
10,
1L,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(100),
new ThreadPoolExecutor.CallerRunsPolicy());
List<String> nameList = new ArrayList<String>();
for (int i = 0; i < 1000; i++) {
nameList.add(String.valueOf(i));
}
// 使用JDK 1.8的特性,Lambda表达式: (参数) -> {表达式}
nameList.forEach(name -> CompletableFuture.supplyAsync((Supplier<String>) () -> {
print((String) name); // 封装了业务逻辑
return "success";
}, executor).exceptionally(e -> {
System.out.println(e);
return "false";
}));
executor.shutdown();
while (!executor.isTerminated()) {
System.out.println("no terminated");
try {
System.out.println("我要休眠一下");
TimeUnit.MILLISECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
好了,问题解决了,大家有什么疑问都可以留言一起讨论。
Thanks.
日期:2022-2-11
目的:自定义线程池和线程的名称。
当我们完成了线程池和线程工厂的定义后,我们的ThreadPoolExecutor就可以使用自己定义的了,如下:
// OwlThreadPoolExecutor是使用阿里巴巴推荐的创建线程池的方式,自定义的一个池,使用単例模式的懒汉式
ThreadPoolExecutor executor = OwlThreadPoolExecutor.getThreadPoolExecutorInstance();
线程池代码:
package threadpool;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class OwlThreadPoolExecutor {
/**
* default value
*/
private static int corePoolSite = 5;
private static int maxPoolSite = 10;
private static int queueCapacity = 100;
private static Long keepAliveTime = 1L;
public static volatile ThreadPoolExecutor threadPoolExecutorInstance = null;
private OwlThreadPoolExecutor() {}
public static void initialize(int corePoolSite, int maxPoolSite, int queueCapacity, long keepAliveTime) {
OwlThreadPoolExecutor.corePoolSite = corePoolSite;
OwlThreadPoolExecutor.maxPoolSite = maxPoolSite;
OwlThreadPoolExecutor.queueCapacity = queueCapacity;
OwlThreadPoolExecutor.keepAliveTime = keepAliveTime;
}
public static ThreadPoolExecutor getThreadPoolExecutorInstance() {
if (threadPoolExecutorInstance == null || threadPoolExecutorInstance.isShutdown()) {
synchronized (OwlThreadPoolExecutor.class) {
// double check
if (threadPoolExecutorInstance == null || threadPoolExecutorInstance.isShutdown()) {
System.out.println("The thread pool instance is empty, so need to create.");
threadPoolExecutorInstance = new ThreadPoolExecutor(
corePoolSite,
maxPoolSite,
keepAliveTime,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(queueCapacity),
new OwlThreadFactory("ThreadPool"),
new ThreadPoolExecutor.CallerRunsPolicy());
System.out.println("The thread pool instance info: " + threadPoolExecutorInstance);
}
}
}
return threadPoolExecutorInstance;
}
}
线程工厂代码:
自定义线程工厂并给予名字,方便监控JVM的工具追踪问题。
package threadpool;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
public class OwlThreadFactory implements ThreadFactory {
private String namePrefix;
private final AtomicInteger nextId = new AtomicInteger(1);
public OwlThreadFactory(String whatFeatureOfGroup) {
namePrefix = "From OwlThreadFactory's " + whatFeatureOfGroup + "-Worker-"; // 线程池名字 + 线程名字前缀
}
@Override
public Thread newThread(Runnable task) {
String name = namePrefix + nextId.getAndIncrement(); // 线程id
Thread thread = new Thread(null, task, name);
return thread;
}
}
使用自定义线程池后的异步代码:
public static void useCompletableFuture_simple() {
// 这个方法时描述利用1.8新特性,简单使用CompletableFuture实现异步操作
// OwlThreadPoolExecutor是使用阿里巴巴推荐的创建线程池的方式,自定义的一个池,使用単例模式的懒汉式
ThreadPoolExecutor executor = OwlThreadPoolExecutor.getThreadPoolExecutorInstance();
List<String> nameList = new ArrayList<String>();
for (int i = 0; i < 1000; i++) {
nameList.add(String.valueOf(i));
}
// 使用JDK 1.8的特性,Lambda表达式: (参数) -> {表达式}
nameList.forEach(name -> CompletableFuture.supplyAsync((Supplier<String>) () -> {
print(Thread.currentThread().getName() + ", name=" + (String) name); // 封装了业务逻辑
return "success";
}, executor).exceptionally(e -> {
System.out.println(e);
return "false";
}));
// executor.shutdown();
//
// while (!executor.isTerminated()) {
// System.out.println("no terminated");
// try {
// System.out.println("我要休眠一下");
// TimeUnit.MILLISECONDS.sleep(10);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// }
}
说明一下后面的代码为何注释?
// executor.shutdown();
//
// while (!executor.isTerminated()) {
// System.out.println("no terminated");
// try {
// System.out.println("我要休眠一下");
// TimeUnit.MILLISECONDS.sleep(10);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// }
首先这个线程池是公用的,生命周期是和server一致的,所以不能调用shutdown()。
还有就是异步一般是不关心执行结果的,即我交给线程去执行,但是我不关心它什么时候执行完,调用isTerminated(),是在先shutdown之后,看线程是否执行完任务了。这样就相当于同步了,因为要等它执行完成。
main方法不变,运行结果如下:
From OwlThreadFactory's ThreadPool-Worker-1, name=0: Hello World!
From OwlThreadFactory's ThreadPool-Worker-2, name=1: Hello World!
From OwlThreadFactory's ThreadPool-Worker-3, name=2: Hello World!
From OwlThreadFactory's ThreadPool-Worker-4, name=3: Hello World!
From OwlThreadFactory's ThreadPool-Worker-5, name=4: Hello World!
From OwlThreadFactory's ThreadPool-Worker-3, name=8: Hello World!
From OwlThreadFactory's ThreadPool-Worker-5, name=9: Hello World!
From OwlThreadFactory's ThreadPool-Worker-2, name=7: Hello World!
From OwlThreadFactory's ThreadPool-Worker-4, name=6: Hello World!
From OwlThreadFactory's ThreadPool-Worker-1, name=5: Hello World!
From OwlThreadFactory's ThreadPool-Worker-4, name=13: Hello World!
main, name=142: Hello World!
从输出结果我们可以看到线程的名字是我们自定义的。输出了线程名有发现其实主线程(main)也参数了任务的执行,可能是任务量多。
关于ThreadPoolExecutor类的构造器参数后面再出一篇测试看看。