1 Executor概念
从JDK5开始提供了Executor FrameWork给我们,位于 java.util.concurrent 包下,它最重要的是解决了以下两个问题:
- 分离任务的创建和执行者的创建
- 线程重复利用(new线程代价很大)
1.1 共享线程池的概念
-
预先设置好多个Thread,可弹性增加。需要Thread数量多,它就自动增加;需要的Thread数量少,它就自动衰退、消亡。
-
这些Thread可以多次执行很多很小的任务,运行结束后这些Thread又会变成空闲状态
-
任务创建和执行过程解耦,程序员无需关心线程池执行任务的过程。
我们只需要创建好任务,然后丢给线程池去执行。
1.2 Executor主要类
-
Executors:用来创建线程池
Executors.newCachedThreadPool(); // 创建动态增长的线程池 Executors.newFixedThreadPool(4); // 创建固定数量的线程池,一般为CPU核心数的2/4倍
-
ExecutorService:线程池服务
-
Callable:具体的逻辑对象(线程类)
Callable和Runnable是等价的,可以用来执行一个任务。Runnable的run方法没有返回值,而Callable的call方法有返回值。
-
Future:返回结果
2 Executor用法
2.1 例子1
Main:
public class Main {
public static void main(String[] args) throws InterruptedException {
// 创建一个执行服务器
Server server = new Server();
// 创建100个任务,并发给执行器,等待完成
for (int i = 0; i < 100; i++) {
Task task = new Task("task" + i);
Thread.sleep(10);
server.submitTask(task);
}
// 关闭线程池
server.endServer();
}
}
Server:
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
public class Server {
private ThreadPoolExecutor executor;
public Server() {
// 创建一个没有参数的线程池,线程池容量随着任务的量自动增长
executor = (ThreadPoolExecutor) Executors.newCachedThreadPool();
//executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(5);
}
public void submitTask(Task task) {
System.out.println("Server: A new task has arrived");
executor.execute(task);
System.out.println("Server: Pool Size: " + executor.getPoolSize());
System.out.println("Server: Active Count: " + executor.getActiveCount());
System.out.println("Server: Completed Tasks: " + executor.getCompletedTaskCount());
}
public void endServer() {
executor.shutdown();
}
}
Task:
import java.util.Date;
public class Task implements Runnable {
private String name;
public Task(String name) {
this.name = name;
}
@Override
public void run() {
try {
int duration = (int)(Math.random() * 1000);
System.out.printf("%s: Task %s: Doing a task %d seconds\n", Thread.currentThread().getName(), name, duration);
Thread.sleep(duration);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("%s: Task %s: Finished on: \n", Thread.currentThread().getName(), name, new Date());
}
}
我们不用去关心线程池里面如何去执行这100个任务,而且需要多少个线程也并不知道,线程池会自动启动线程去执行,而且如何将第几号任务分配给那个线程执行我们也不需要关心,这个是Executor框架来做。
2.2 例子2
计算1~10000的和,分10个任务执行。
SumTest:
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
public class SumTest {
public static void main(String[] args) {
// 执行线程池
ThreadPoolExecutor executor =
(ThreadPoolExecutor) Executors.newFixedThreadPool(4);
List<Future<Integer>> resultList = new ArrayList<>();
for (int i = 0; i < 10; i++) {
SumTask calculator = new SumTask(i*1000+1, (i+1)*1000);
Future<Integer> result = executor.submit(calculator);
resultList.add(result);
}
/* 每隔50ms 轮询等待10个任务结束 */
do {
//System.out.printf("Main: 已经完成 %d 个任务\n", executor.getCompletedTaskCount());
//for (int i = 0; i < resultList.size(); i++) {
// // result的isDone 方法是true的话就表明这个线程已经完成了任务
// System.out.printf("Main: Task %d: %s\n", i, resultList.get(i).isDone());
//}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
} while (executor.getCompletedTaskCount() < resultList.size());
/* 所有任务都已经结束了,计算结果*/
int total = 0;
for (int i = 0; i < resultList.size(); i++) {
try {
Integer sum = resultList.get(i).get();
total += sum;
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
System.out.println("Total: " + total);
// 关闭线程池
executor.shutdown();
}
}
SumTask:
import java.util.concurrent.Callable;
public class SumTask implements Callable<Integer> {
// 定义每个线程的计算区间
private int startNum;
private int endNum;
public SumTask(int startNum, int endNum) {
this.startNum = startNum;
this.endNum = endNum;
}
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = startNum; i <= endNum; i++) {
sum += i;
}
System.out.printf("%s %d\n", Thread.currentThread().getName(), sum);
return sum;
}
}
执行结果:
