Executors
创建线程池的类,提供四种线程池:
- newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程
示例代码:
public class CallableDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
Callable<String> c = () -> "Hello Callable";
ExecutorService service = Executors.newCachedThreadPool();
//异步
Future<String> future = service.submit(c);
//阻塞
System.out.println(future.get());
service.shutdown();
}
}
- newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待
- newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行
- newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行
Callable
Callable是一个任务,类似于Runnable,但是Callable任务是有返回值的,一般用线程池去执行这个Callable任务,返回一个包含Callable执行结果的Future,这个操作是异步的,然后通过Future.get()这个阻塞方法去获取执行结果
Future
存储执行任务产生的结果
FutureTask
代替Callable任务+Future,FutureTask实现了RunnableFuture接口,而RunnableFuture接口同时继承了Runnable和Future接口(接口可以同时继承2个以上接口),所以FutureTask本身就是一个任务和Future,简单使用如下
public class FutureTaskDemo {
public static void main(String[] args) throws InterruptedException, ExecutionException {
FutureTask<Integer> task = new FutureTask<>(() -> {
TimeUnit.MILLISECONDS.sleep(500);
return 1000;
}); //new Callable () { Integer call();}
new Thread(task).start();//这里也可以用线程池
//阻塞
System.out.println(task.get());
}
}
CompletableFuture
用来管理多个Future的结果,简单使用如下
public class CompletableFutureDemo {
public static void main(String[] args) {
long start, end;
start = System.currentTimeMillis();
CompletableFuture<Double> futureTM = CompletableFuture.supplyAsync(CompletableFutureDemo::priceOfTM);
CompletableFuture<Double> futureTB = CompletableFuture.supplyAsync(CompletableFutureDemo::priceOfTB);
CompletableFuture<Double> futureJD = CompletableFuture.supplyAsync(CompletableFutureDemo::priceOfJD);
CompletableFuture.allOf(futureTM, futureTB, futureJD).join();
end = System.currentTimeMillis();
System.out.println("use completable future! " + (end - start));
}
private static double priceOfTM() {
delay();
return 1.00;
}
private static double priceOfTB() {
delay();
return 2.00;
}
private static double priceOfJD() {
delay();
return 3.00;
}
private static void delay() {
int time = new Random().nextInt(500);
try {
TimeUnit.MILLISECONDS.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("After %s sleep!\n", time);
}
}
ThreadPoolExecutor及七个参数
创建线程池构造方法7个参数,如下图
- corePoolSize 线程池核心线程大小 处于空闲也不会被回收的线程
- maximumPoolSize 线程池最大线程数量 达到空闲时间就会被回收的线程
- keepAliveTime 空闲线程存活时间
- unit 空间线程存活时间单位
- workQueue 工作队列
新任务被提交后,会先进入到此工作队列中,任务调度时再从队列中取出任务。jdk中提供了四种工作队列:
①ArrayBlockingQueue
基于数组的有界阻塞队列,按FIFO排序。新任务进来后,会放到该队列的队尾,有界的数组可以防止资源耗尽问题。当线程池中线程数量达到corePoolSize后,再有新任务进来,则会将任务放入该队列的队尾,等待被调度。如果队列已经是满的,则创建一个新线程,如果线程数量已经达到maxPoolSize,则会执行拒绝策略。
②LinkedBlockingQuene
基于链表的无界阻塞队列(其实最大容量为Interger.MAX),按照FIFO排序。由于该队列的近似无界性,当线程池中线程数量达到corePoolSize后,再有新任务进来,会一直存入该队列,而不会去创建新线程直到maxPoolSize,因此使用该工作队列时,参数maxPoolSize其实是不起作用的。
③SynchronousQuene
一个不缓存任务的阻塞队列,生产者放入一个任务必须等到消费者取出这个任务。也就是说新任务进来时,不会缓存,而是直接被调度执行该任务,如果没有可用线程,则创建新线程,如果线程数量达到maxPoolSize,则执行拒绝策略。
④PriorityBlockingQueue
具有优先级的无界阻塞队列,优先级通过参数Comparator实现
- threadFactory 线程工厂
创建一个新线程时使用的工厂,可以用来设定线程名、是否为daemon线程等等
- handler 拒绝策略
当工作队列中的任务已到达最大限制,并且线程池中的线程数量也达到最大限制,这时如果有新任务提交进来,该如何处理呢。这里的拒绝策略,就是解决这个问题的,jdk中提供了4中拒绝策略:
①CallerRunsPolicy
该策略下,在调用者线程中直接执行被拒绝任务的run方法,除非线程池已经shutdown,则直接抛弃任务。
②AbortPolicy
该策略下,直接丢弃任务,并抛出RejectedExecutionException异常。
③DiscardPolicy
该策略下,直接丢弃任务,什么都不做。
④DiscardOldestPolicy
该策略下,抛弃进入队列最早的那个任务,然后尝试把这次拒绝的任务放入队列
简单使用代码如下:
public class ThreadPoolExecutorDemo {
static class Task implements Runnable {
private int i;
public Task(int i) {
this.i = i;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " Task " + i);
try {
System.in.read();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public String toString() {
return "Task{" +
"i=" + i +
'}';
}
}
public static void main(String[] args) {
ThreadPoolExecutor tpe = new ThreadPoolExecutor(2, 4,
60, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(4),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.CallerRunsPolicy());
for (int i = 0; i < 8; i++) {
tpe.execute(new Task(i));
}
System.out.println(tpe.getQueue());
tpe.execute(new Task(100));
System.out.println(tpe.getQueue());
tpe.shutdown();
}