ExecutorService
五种线程池的适应场景
- newCachedThreadPool:用来创建一个可以无限扩大的线程池,适用于服务器负载较轻,执行很多短期异步任务。
- newFixedThreadPool:创建一个固定大小的线程池,因为采用无界的阻塞队列,所以实际线程数量永远不会变化,适用于可以预测线程数量的业务中,或者服务器负载较重,对当前线程数量进行限制。
- newSingleThreadExecutor:创建一个单线程的线程池,适用于需要保证顺序执行各个任务,并且在任意时间点,不会有多个线程是活动的场景。
- newScheduledThreadPool:可以延时启动,定时启动的线程池,适用于需要多个后台线程执行周期任务的场景。
- newWorkStealingPool:创建一个拥有多个任务队列的线程池,可以减少连接数,创建当前可用cpu数量的线程来并行执行,适用于大耗时的操作,可以并行来执行
Runnable、Callable、FutureTask

相同点:都属于线程池中要被运行的任务;
不同点:
Runnable是无返回值的任务,可以在线程中使用
Callable是有返回值的任务 ,不可以在线程中使用
FutureTask是有返回值,而且更易于管理和控制的任务,不可以在线程中使用;
前两者通过查看他们类可以很清楚的知道:
public interface Runnable {
/**这个任务运行完之后没有返回值*/
public abstract void run();
}
-----------------------------------------------
public interface Callable<V> {
/**这个任务运行完之后返回泛型 V*/
V call() throws Exception;
}
而FutureTask稍微复杂一点,其实看看它的类结构:
![]()
Runnable
public class MyRunnable implements Runnable {
@Override
public void run() {
for (int x = 0; x < 100; x++) {
System.out.println(Thread.currentThread().getName() + ":" + x);
}
}
}
----------------------------------------
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/*
* 线程池的好处:线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。
*
* 如何实现线程的代码呢?
* A:创建一个线程池对象,控制要创建几个线程对象。
* public static ExecutorService newFixedThreadPool(int nThreads)
* B:这种线程池的线程可以执行:
* 可以执行Runnable对象或者Callable对象代表的线程
* 做一个类实现Runnable接口。
* C:调用如下方法即可
* Future<?> submit(Runnable task)
* <T> Future<T> submit(Callable<T> task)
* D:我就要结束,可以吗?
* 可以。
*/
public class ExecutorsDemo {
public static void main(String[] args) {
// 创建一个线程池对象,控制要创建几个线程对象。
// public static ExecutorService newFixedThreadPool(int nThreads)
ExecutorService pool = Executors.newFixedThreadPool(2);
// 可以执行Runnable对象或者Callable对象代表的线程
pool.submit(new MyRunnable());
pool.submit(new MyRunnable());
//结束线程池
pool.shutdown();
}
}
关于shutdown的一点补充
1、shutdown():停止接收新任务,原来的任务继续执行
2、shutdownNow():停止接收新任务,原来的任务停止执行
3、awaitTermination(long timeOut, TimeUnit unit):当前线程阻塞
当前线程阻塞,直到:
- 等所有已提交的任务(包括正在跑的和队列中等待的)执行完;
- 或者 等超时时间到了(timeout 和 TimeUnit设定的时间);
- 或者 线程被中断,抛出InterruptedException
Callable
Callable+Future:
Future<V>接口是用来获取异步计算结果的,说白了就是对具体的Runnable或者Callable对象任务执行的结果进行获取(get()),取消(cancel()),判断是否完成等操
class MyCallable implements Callable<Object> {
private String taskNum;
MyCallable(String taskNum) {
this.taskNum = taskNum;
}
public Object call() throws Exception {
System.out.println(">>>" + taskNum + "任务启动");
Date dateTmp1 = new Date();
Thread.sleep(1000);
Date dateTmp2 = new Date();
long time = dateTmp2.getTime() - dateTmp1.getTime();
System.out.println(">>>" + taskNum + "任务终止");
return taskNum + "任务返回运行结果,当前任务时间【" + time + "毫秒】";
}
}
----------------------------------------------------------------
public static void main(String[] args) throws ExecutionException,
InterruptedException {
System.out.println("----程序开始运行----");
Date date1 = new Date();
// 创建一个线程池
ExecutorService pool = Executors.newFixedThreadPool(5);
// 创建多个有返回值的任务
List<Future> list = new ArrayList<Future>();
for (int i = 0; i < taskSize; i++) {
Callable c = new MyCallable(i + " ");
// 执行任务并获取Future对象
Future f = pool.submit(c);
// System.out.println(">>>" + f.get().toString());
list.add(f);
}
// 关闭线程池
pool.shutdown();
// 获取所有并发任务的运行结果
for (Future f : list) {
// 从Future对象上获取任务的返回值,并输出到控制台
System.out.println(">>>" + f.get().toString());
}
Date date2 = new Date();
System.out.println("----程序结束运行----,程序运行时间【"
+ (date2.getTime() - date1.getTime()) + "毫秒】");
}
}
使用JDK中Executors.newFixedThreadPool方法创建ExecutorService,ExecutorService的submit方法接收Callable接口的实现,JDK内部将弄成线程处理,使用Future接收submit方法的返回值,当future调用get方法时,如果线程还没有执行完,程序阻塞在这里,直到线程执行完。
FutureTask
Future VS FutureTask
使用Future时,我们需要实现Callable接口,并通过ExecutorService接口的submit方法获取返回的Future对象。
使用FutureTask时,根据FutureTask的构造函数可以看到FutureTask既可以接收Callable的实现类,也可以接收Runnable的实现类。当你传入的是Callable的实现类时,可以获取线程执行的结果;传入Runnable的实现类时,由于Runnable的实现没有返回值,需要传入一个你设置的线程完成标识,也就是result,然后当线程结束时会把你传入的result原值返回给你。
我们先来看看FutureTask的实现
public class FutureTask<V> implements RunnableFuture<V> {
FutureTask类实现了RunnableFuture接口,我们看一下RunnableFuture接口的实现:
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
分析:FutureTask除了实现了Future接口外还实现了Runnable接口(即可以通过Runnable接口实现线程,也可以通过Future取得线程执行完后的结果),因此FutureTask也可以直接提交给Executor执行。
最后我们给出FutureTask的两种构造函数:
public FutureTask(Callable<V> callable) {
}
public FutureTask(Runnable runnable, V result) {
}
Callable+FutureTask:
package com.zejian.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
/**
* @author zejian
* @time 2016年3月15日 下午2:05:43
* @decrition callable执行测试类
*/
public class CallableTest {
public static void main(String[] args) {
// //创建线程池
// ExecutorService es = Executors.newSingleThreadExecutor();
// //创建Callable对象任务
// CallableDemo calTask=new CallableDemo();
// //提交任务并获取执行结果
// Future<Integer> future =es.submit(calTask);
// //关闭线程池
// es.shutdown();
//创建线程池
ExecutorService es = Executors.newSingleThreadExecutor();
//创建Callable对象任务
CallableDemo calTask=new CallableDemo();
//创建FutureTask
FutureTask<Integer> futureTask=new FutureTask<>(calTask);
//执行任务
es.submit(futureTask);
//关闭线程池
es.shutdown();
try {
Thread.sleep(2000);
System.out.println("主线程在执行其他任务");
if(futureTask.get()!=null){
//输出获取到的结果
System.out.println("futureTask.get()-->"+futureTask.get());
}else{
//输出获取到的结果
System.out.println("futureTask.get()未获取到结果");
}
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("主线程在执行完成");
}
}
执行结果:
Callable子线程开始计算啦!
主线程在执行其他任务
Callable子线程计算结束!
futureTask.get()-->12497500
主线程在执行完成
也可
Callable+Future:参考上面Callable的内容
本文详细解析了Java中线程池的五种类型及其适用场景,深入探讨了ExecutorService的使用,包括newCachedThreadPool、newFixedThreadPool、newSingleThreadExecutor、newScheduledThreadPool和newWorkStealingPool的特点。同时,对比了Runnable、Callable与FutureTask的区别,以及它们在多线程编程中的应用。

2381

被折叠的 条评论
为什么被折叠?



